blob: 5124ac9f1818da33cdb20397e6ac6ec0cb30e442 [file] [log] [blame]
/*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* You should also find the complete GPL in the COPYING file accompanying
* this source code.
*/
#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 = comedi_board(dev);
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 = comedi_board(dev);
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 = comedi_board(dev);
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 = comedi_board(dev);
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 = comedi_board(dev);
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);
devpriv->s_EeParameters.i_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 = comedi_board(dev);
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;
}
}
}