aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/comedi/drivers/addi-data/addi_eeprom.c
blob: b731856c27da1d6a120d34df8cd8fe04c4ecc6e1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
154
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
189
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
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
/*
 * addi_eeprom.c - ADDI EEPROM Module
 * Copyright (C) 2004,2005  ADDI-DATA GmbH for the source code of this module.
 * Project manager: Eric Stolz
 *
 *	ADDI-DATA GmbH
 *	Dieselstrasse 3
 *	D-77833 Ottersweier
 *	Tel: +19(0)7223/9493-0
 *	Fax: +49(0)7223/9493-92
 *	http://www.addi-data.com
 *	info@addi-data.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 */

#include <linux/delay.h>

#define NVRAM_USER_DATA_START	0x100

#define NVCMD_BEGIN_READ	(0x7 << 5)	/* nvRam begin read command */
#define NVCMD_LOAD_LOW		(0x4 << 5)	/* nvRam load low command */
#define NVCMD_LOAD_HIGH		(0x5 << 5)	/* nvRam load high command */

#define EE93C76_CLK_BIT		(1 << 0)
#define EE93C76_CS_BIT		(1 << 1)
#define EE93C76_DOUT_BIT	(1 << 2)
#define EE93C76_DIN_BIT		(1 << 3)
#define EE93C76_READ_CMD	(0x0180 << 4)
#define EE93C76_CMD_LEN		13

#define EEPROM_DIGITALINPUT		0
#define EEPROM_DIGITALOUTPUT		1
#define EEPROM_ANALOGINPUT		2
#define EEPROM_ANALOGOUTPUT		3
#define EEPROM_TIMER			4
#define EEPROM_WATCHDOG			5
#define EEPROM_TIMER_WATCHDOG_COUNTER	10

static void addi_eeprom_clk_93c76(unsigned long iobase, unsigned int val)
{
	outl(val & ~EE93C76_CLK_BIT, iobase);
	udelay(100);

	outl(val | EE93C76_CLK_BIT, iobase);
	udelay(100);
}

static unsigned int addi_eeprom_cmd_93c76(unsigned long iobase,
					  unsigned int cmd,
					  unsigned char len)
{
	unsigned int val = EE93C76_CS_BIT;
	int i;

	/* Toggle EEPROM's Chip select to get it out of Shift Register Mode */
	outl(val, iobase);
	udelay(100);

	/* Send EEPROM command - one bit at a time */
	for (i = (len - 1); i >= 0; i--) {
		if (cmd & (1 << i))
			val |= EE93C76_DOUT_BIT;
		else
			val &= ~EE93C76_DOUT_BIT;

		/* Write the command */
		outl(val, iobase);
		udelay(100);

		addi_eeprom_clk_93c76(iobase, val);
	}
	return val;
}

static unsigned short addi_eeprom_readw_93c76(unsigned long iobase,
					      unsigned short addr)
{
	unsigned short val = 0;
	unsigned int cmd;
	unsigned int tmp;
	int i;

	/* Send EEPROM read command and offset to EEPROM */
	cmd = EE93C76_READ_CMD | (addr / 2);
	cmd = addi_eeprom_cmd_93c76(iobase, cmd, EE93C76_CMD_LEN);

	/* Get the 16-bit value */
	for (i = 0; i < 16; i++) {
		addi_eeprom_clk_93c76(iobase, cmd);

		tmp = inl(iobase);
		udelay(100);

		val <<= 1;
		if (tmp & EE93C76_DIN_BIT)
			val |= 0x1;
	}

	/* Toggle EEPROM's Chip select to get it out of Shift Register Mode */
	outl(0, iobase);
	udelay(100);

	return val;
}

static void addi_eeprom_nvram_wait(unsigned long iobase)
{
	unsigned char val;

	do {
		val = inb(iobase + AMCC_OP_REG_MCSR_NVCMD);
	} while (val & 0x80);
}

static unsigned short addi_eeprom_readw_nvram(unsigned long iobase,
					      unsigned short addr)
{
	unsigned short val = 0;
	unsigned char tmp;
	unsigned char i;

	for (i = 0; i < 2; i++) {
		/* Load the low 8 bit address */
		outb(NVCMD_LOAD_LOW, iobase + AMCC_OP_REG_MCSR_NVCMD);
		addi_eeprom_nvram_wait(iobase);
		outb((addr + i) & 0xff, iobase + AMCC_OP_REG_MCSR_NVDATA);
		addi_eeprom_nvram_wait(iobase);

		/* Load the high 8 bit address */
		outb(NVCMD_LOAD_HIGH, iobase + AMCC_OP_REG_MCSR_NVCMD);
		addi_eeprom_nvram_wait(iobase);
		outb(((addr + i) >> 8) & 0xff,
			iobase + AMCC_OP_REG_MCSR_NVDATA);
		addi_eeprom_nvram_wait(iobase);

		/* Read the eeprom data byte */
		outb(NVCMD_BEGIN_READ, iobase + AMCC_OP_REG_MCSR_NVCMD);
		addi_eeprom_nvram_wait(iobase);
		tmp = inb(iobase + AMCC_OP_REG_MCSR_NVDATA);
		addi_eeprom_nvram_wait(iobase);

		if (i == 0)
			val |= tmp;
		else
			val |= (tmp << 8);
	}

	return val;
}

static unsigned short addi_eeprom_readw(unsigned long iobase,
					char *type,
					unsigned short addr)
{
	unsigned short val = 0;

	/* Add the offset to the start of the user data */
	addr += NVRAM_USER_DATA_START;

	if (!strcmp(type, "S5920") || !strcmp(type, "S5933"))
		val = addi_eeprom_readw_nvram(iobase, addr);

	if (!strcmp(type, "93C76"))
		val = addi_eeprom_readw_93c76(iobase, addr);

	return val;
}

static void addi_eeprom_read_di_info(struct comedi_device *dev,
				     unsigned long iobase,
				     unsigned short addr)
{
	const struct addi_board *this_board = dev->board_ptr;
	struct addi_private *devpriv = dev->private;
	char *type = this_board->pc_EepromChip;
	unsigned short tmp;

	/* Number of channels */
	tmp = addi_eeprom_readw(iobase, type, addr + 6);
	devpriv->s_EeParameters.i_NbrDiChannel = tmp;

	/* Interruptible or not */
	tmp = addi_eeprom_readw(iobase, type, addr + 8);
	tmp = (tmp >> 7) & 0x01;

	/* How many interruptible logic */
	tmp = addi_eeprom_readw(iobase, type, addr + 10);
}

static void addi_eeprom_read_do_info(struct comedi_device *dev,
				     unsigned long iobase,
				     unsigned short addr)
{
	const struct addi_board *this_board = dev->board_ptr;
	struct addi_private *devpriv = dev->private;
	char *type = this_board->pc_EepromChip;
	unsigned short tmp;

	/* Number of channels */
	tmp = addi_eeprom_readw(iobase, type, addr + 6);
	devpriv->s_EeParameters.i_NbrDoChannel = tmp;

	devpriv->s_EeParameters.i_DoMaxdata = 0xffffffff >> (32 - tmp);
}

static void addi_eeprom_read_timer_info(struct comedi_device *dev,
					unsigned long iobase,
					unsigned short addr)
{
	struct addi_private *devpriv = dev->private;
#if 0
	const struct addi_board *this_board = dev->board_ptr;
	char *type = this_board->pc_EepromChip;
	unsigned short offset = 0;
	unsigned short ntimers;
	unsigned short tmp;
	int i;

	/* Number of Timers */
	ntimers = addi_eeprom_readw(iobase, type, addr + 6);

	/* Read header size */
	for (i = 0; i < ntimers; i++) {
		unsigned short size;
		unsigned short res;
		unsigned short mode;
		unsigned short min_timing;
		unsigned short timebase;

		size = addi_eeprom_readw(iobase, type, addr + 8 + offset + 0);

		/* Resolution / Mode */
		tmp = addi_eeprom_readw(iobase, type, addr + 8 + offset + 2);
		res = (tmp >> 10) & 0x3f;
		mode = (tmp >> 4) & 0x3f;

		/* MinTiming / Timebase */
		tmp = addi_eeprom_readw(iobase, type, addr + 8 + offset + 4);
		min_timing = (tmp  >> 6) & 0x3ff;
		Timebase = tmp & 0x3f;

		offset += size;
	}
#endif
	/* Timer subdevice present */
	devpriv->s_EeParameters.i_Timer = 1;
}

static void addi_eeprom_read_ao_info(struct comedi_device *dev,
				     unsigned long iobase,
				     unsigned short addr)
{
	const struct addi_board *this_board = dev->board_ptr;
	struct addi_private *devpriv = dev->private;
	char *type = this_board->pc_EepromChip;
	unsigned short tmp;

	/* No of channels for 1st hard component */
	tmp = addi_eeprom_readw(iobase, type, addr + 10);
	devpriv->s_EeParameters.i_NbrAoChannel = (tmp >> 4) & 0x3ff;

	/* Resolution for 1st hard component */
	tmp = addi_eeprom_readw(iobase, type, addr + 16);
	tmp = (tmp >> 8) & 0xff;
	devpriv->s_EeParameters.i_AoMaxdata = 0xfff >> (16 - tmp);
}

static void addi_eeprom_read_ai_info(struct comedi_device *dev,
				     unsigned long iobase,
				     unsigned short addr)
{
	const struct addi_board *this_board = dev->board_ptr;
	struct addi_private *devpriv = dev->private;
	char *type = this_board->pc_EepromChip;
	unsigned short offset;
	unsigned short tmp;

	/* No of channels for 1st hard component */
	tmp = addi_eeprom_readw(iobase, type, addr + 10);
	devpriv->s_EeParameters.i_NbrAiChannel = (tmp >> 4) & 0x3ff;
	if (!strcmp(this_board->pc_DriverName, "apci3200"))
		devpriv->s_EeParameters.i_NbrAiChannel *= 4;

	tmp = addi_eeprom_readw(iobase, type, addr + 16);
	devpriv->s_EeParameters.ui_MinAcquisitiontimeNs = tmp * 1000;

	tmp = addi_eeprom_readw(iobase, type, addr + 30);
	devpriv->s_EeParameters.ui_MinDelaytimeNs = tmp * 1000;

	tmp = addi_eeprom_readw(iobase, type, addr + 20);
	/* dma = (tmp >> 13) & 0x01; */

	tmp = addi_eeprom_readw(iobase, type, addr + 72) & 0xff;
	if (tmp) {		/* > 0 */
		/* offset of first analog input single header */
		offset = 74 + (2 * tmp) + (10 * (1 + (tmp / 16)));
	} else {		/* = 0 */
		offset = 74;
	}

	/* Resolution */
	tmp = addi_eeprom_readw(iobase, type, addr + offset + 2) & 0x1f;
	devpriv->s_EeParameters.i_AiMaxdata = 0xffff >> (16 - tmp);
}

static void addi_eeprom_read_info(struct comedi_device *dev,
				  unsigned long iobase)
{
	const struct addi_board *this_board = dev->board_ptr;
	char *type = this_board->pc_EepromChip;
	unsigned short size;
	unsigned char nfuncs;
	int i;

	size = addi_eeprom_readw(iobase, type, 8);
	nfuncs = addi_eeprom_readw(iobase, type, 10) & 0xff;

	/* Read functionality details */
	for (i = 0; i < nfuncs; i++) {
		unsigned short offset = i * 4;
		unsigned short addr;
		unsigned char func;

		func = addi_eeprom_readw(iobase, type, 12 + offset) & 0x3f;
		addr = addi_eeprom_readw(iobase, type, 14 + offset);

		switch (func) {
		case EEPROM_DIGITALINPUT:
			addi_eeprom_read_di_info(dev, iobase, addr);
			break;

		case EEPROM_DIGITALOUTPUT:
			addi_eeprom_read_do_info(dev, iobase, addr);
			break;

		case EEPROM_ANALOGINPUT:
			addi_eeprom_read_ai_info(dev, iobase, addr);
			break;

		case EEPROM_ANALOGOUTPUT:
			addi_eeprom_read_ao_info(dev, iobase, addr);
			break;

		case EEPROM_TIMER:
		case EEPROM_WATCHDOG:
		case EEPROM_TIMER_WATCHDOG_COUNTER:
			addi_eeprom_read_timer_info(dev, iobase, addr);
			break;
		}
	}
}