patch-2.4.20 linux-2.4.20/drivers/net/e100/e100_eeprom.c
Next file: linux-2.4.20/drivers/net/e100/e100_main.c
Previous file: linux-2.4.20/drivers/net/e100/e100_config.h
Back to the patch index
Back to the overall index
- Lines: 566
- Date:
Thu Nov 28 15:53:13 2002
- Orig file:
linux-2.4.19/drivers/net/e100/e100_eeprom.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -urN linux-2.4.19/drivers/net/e100/e100_eeprom.c linux-2.4.20/drivers/net/e100/e100_eeprom.c
@@ -0,0 +1,565 @@
+/*******************************************************************************
+
+
+ Copyright(c) 1999 - 2002 Intel Corporation. All rights reserved.
+
+ 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.
+
+ The full GNU General Public License is included in this distribution in the
+ file called LICENSE.
+
+ Contact Information:
+ Linux NICS <linux.nics@intel.com>
+ Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
+*******************************************************************************/
+
+/**********************************************************************
+* *
+* INTEL CORPORATION *
+* *
+* This software is supplied under the terms of the license included *
+* above. All use of this driver must be in accordance with the terms *
+* of that license. *
+* *
+* Module Name: e100_eeprom.c *
+* *
+* Abstract: This module contains routines to read and write to a *
+* serial EEPROM *
+* *
+* Environment: This file is intended to be specific to the Linux *
+* operating system. *
+* *
+**********************************************************************/
+#include "e100.h"
+
+#define CSR_EEPROM_CONTROL_FIELD(bdp) ((bdp)->scb->scb_eprm_cntrl)
+
+#define CSR_GENERAL_CONTROL2_FIELD(bdp) \
+ ((bdp)->scb->scb_ext.d102_scb.scb_gen_ctrl2)
+
+#define EEPROM_STALL_TIME 4
+#define EEPROM_CHECKSUM ((u16) 0xBABA)
+#define EEPROM_MAX_WORD_SIZE 256
+
+void e100_eeprom_cleanup(struct e100_private *adapter);
+u16 e100_eeprom_calculate_chksum(struct e100_private *adapter);
+static void e100_eeprom_write_word(struct e100_private *adapter, u16 reg,
+ u16 data);
+void e100_eeprom_write_block(struct e100_private *adapter, u16 start, u16 *data,
+ u16 size);
+u16 e100_eeprom_size(struct e100_private *adapter);
+u16 e100_eeprom_read(struct e100_private *adapter, u16 reg);
+
+static void shift_out_bits(struct e100_private *adapter, u16 data, u16 count);
+static u16 shift_in_bits(struct e100_private *adapter);
+static void raise_clock(struct e100_private *adapter, u16 *x);
+static void lower_clock(struct e100_private *adapter, u16 *x);
+static u16 eeprom_wait_cmd_done(struct e100_private *adapter);
+static void eeprom_stand_by(struct e100_private *adapter);
+
+//----------------------------------------------------------------------------------------
+// Procedure: eeprom_set_semaphore
+//
+// Description: This function set (write 1) Gamla EEPROM semaphore bit (bit 23 word 0x1C in the CSR).
+//
+// Arguments:
+// Adapter - Adapter context
+//
+// Returns: true if success
+// else return false
+//
+//----------------------------------------------------------------------------------------
+
+inline u8
+eeprom_set_semaphore(struct e100_private *adapter)
+{
+ u16 data = 0;
+ unsigned long expiration_time = jiffies + HZ / 100 + 1;
+
+ do {
+ // Get current value of General Control 2
+ data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter));
+
+ // Set bit 23 word 0x1C in the CSR.
+ data |= SCB_GCR2_EEPROM_ACCESS_SEMAPHORE;
+ writeb(data, &CSR_GENERAL_CONTROL2_FIELD(adapter));
+
+ // Check to see if this bit set or not.
+ data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter));
+
+ if (data & SCB_GCR2_EEPROM_ACCESS_SEMAPHORE) {
+ return true;
+ }
+
+ if (time_before(jiffies, expiration_time))
+ yield();
+ else
+ return false;
+
+ } while (true);
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: eeprom_reset_semaphore
+//
+// Description: This function reset (write 0) Gamla EEPROM semaphore bit
+// (bit 23 word 0x1C in the CSR).
+//
+// Arguments: struct e100_private * adapter - Adapter context
+//----------------------------------------------------------------------------------------
+
+inline void
+eeprom_reset_semaphore(struct e100_private *adapter)
+{
+ u16 data = 0;
+
+ data = readb(&CSR_GENERAL_CONTROL2_FIELD(adapter));
+ data &= ~(SCB_GCR2_EEPROM_ACCESS_SEMAPHORE);
+ writeb(data, &CSR_GENERAL_CONTROL2_FIELD(adapter));
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: e100_eeprom_size
+//
+// Description: This routine determines the size of the EEPROM. This value should be
+// checked for validity - ie. is it too big or too small. The size returned
+// is then passed to the read/write functions.
+//
+// Returns:
+// Size of the eeprom, or zero if an error occured
+//----------------------------------------------------------------------------------------
+u16
+e100_eeprom_size(struct e100_private *adapter)
+{
+ u16 x, size = 1; // must be one to accumulate a product
+
+ // if we've already stored this data, read from memory
+ if (adapter->eeprom_size) {
+ return adapter->eeprom_size;
+ }
+ // otherwise, read from the eeprom
+ // Set EEPROM semaphore.
+ if (adapter->rev_id >= D102_REV_ID) {
+ if (!eeprom_set_semaphore(adapter))
+ return 0;
+ }
+ // enable the eeprom by setting EECS.
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ x &= ~(EEDI | EEDO | EESK);
+ x |= EECS;
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ // write the read opcode
+ shift_out_bits(adapter, EEPROM_READ_OPCODE, 3);
+
+ // experiment to discover the size of the eeprom. request register zero
+ // and wait for the eeprom to tell us it has accepted the entire address.
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ do {
+ size *= 2; // each bit of address doubles eeprom size
+ x |= EEDO; // set bit to detect "dummy zero"
+ x &= ~EEDI; // address consists of all zeros
+
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status));
+ udelay(EEPROM_STALL_TIME);
+ raise_clock(adapter, &x);
+ lower_clock(adapter, &x);
+
+ // check for "dummy zero"
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ if (size > EEPROM_MAX_WORD_SIZE) {
+ size = 0;
+ break;
+ }
+ } while (x & EEDO);
+
+ // read in the value requested
+ (void) shift_in_bits(adapter);
+ e100_eeprom_cleanup(adapter);
+
+ // Clear EEPROM Semaphore.
+ if (adapter->rev_id >= D102_REV_ID) {
+ eeprom_reset_semaphore(adapter);
+ }
+
+ return size;
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: eeprom_address_size
+//
+// Description: determines the number of bits in an address for the eeprom acceptable
+// values are 64, 128, and 256
+// Arguments: size of the eeprom
+// Returns: bits in an address for that size eeprom
+//----------------------------------------------------------------------------------------
+
+static inline int
+eeprom_address_size(u16 size)
+{
+ int isize = size;
+
+ return (ffs(isize) - 1);
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: e100_eeprom_read
+//
+// Description: This routine serially reads one word out of the EEPROM.
+//
+// Arguments:
+// adapter - our adapter context
+// reg - EEPROM word to read.
+//
+// Returns:
+// Contents of EEPROM word (reg).
+//----------------------------------------------------------------------------------------
+
+u16
+e100_eeprom_read(struct e100_private *adapter, u16 reg)
+{
+ u16 x, data, bits;
+
+ // Set EEPROM semaphore.
+ if (adapter->rev_id >= D102_REV_ID) {
+ if (!eeprom_set_semaphore(adapter))
+ return 0;
+ }
+ // eeprom size is initialized to zero
+ if (!adapter->eeprom_size)
+ adapter->eeprom_size = e100_eeprom_size(adapter);
+
+ bits = eeprom_address_size(adapter->eeprom_size);
+
+ // select EEPROM, reset bits, set EECS
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ x &= ~(EEDI | EEDO | EESK);
+ x |= EECS;
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ // write the read opcode and register number in that order
+ // The opcode is 3bits in length, reg is 'bits' bits long
+ shift_out_bits(adapter, EEPROM_READ_OPCODE, 3);
+ shift_out_bits(adapter, reg, bits);
+
+ // Now read the data (16 bits) in from the selected EEPROM word
+ data = shift_in_bits(adapter);
+
+ e100_eeprom_cleanup(adapter);
+
+ // Clear EEPROM Semaphore.
+ if (adapter->rev_id >= D102_REV_ID) {
+ eeprom_reset_semaphore(adapter);
+ }
+
+ return data;
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: shift_out_bits
+//
+// Description: This routine shifts data bits out to the EEPROM.
+//
+// Arguments:
+// data - data to send to the EEPROM.
+// count - number of data bits to shift out.
+//
+// Returns: (none)
+//----------------------------------------------------------------------------------------
+
+static void
+shift_out_bits(struct e100_private *adapter, u16 data, u16 count)
+{
+ u16 x, mask;
+
+ mask = 1 << (count - 1);
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ x &= ~(EEDO | EEDI);
+
+ do {
+ x &= ~EEDI;
+ if (data & mask)
+ x |= EEDI;
+
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status)); /* flush command to card */
+ udelay(EEPROM_STALL_TIME);
+ raise_clock(adapter, &x);
+ lower_clock(adapter, &x);
+ mask = mask >> 1;
+ } while (mask);
+
+ x &= ~EEDI;
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: raise_clock
+//
+// Description: This routine raises the EEPROM's clock input (EESK)
+//
+// Arguments:
+// x - Ptr to the EEPROM control register's current value
+//
+// Returns: (none)
+//----------------------------------------------------------------------------------------
+
+void
+raise_clock(struct e100_private *adapter, u16 *x)
+{
+ *x = *x | EESK;
+ writew(*x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status)); /* flush command to card */
+ udelay(EEPROM_STALL_TIME);
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: lower_clock
+//
+// Description: This routine lower's the EEPROM's clock input (EESK)
+//
+// Arguments:
+// x - Ptr to the EEPROM control register's current value
+//
+// Returns: (none)
+//----------------------------------------------------------------------------------------
+
+void
+lower_clock(struct e100_private *adapter, u16 *x)
+{
+ *x = *x & ~EESK;
+ writew(*x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status)); /* flush command to card */
+ udelay(EEPROM_STALL_TIME);
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: shift_in_bits
+//
+// Description: This routine shifts data bits in from the EEPROM.
+//
+// Arguments:
+//
+// Returns:
+// The contents of that particular EEPROM word
+//----------------------------------------------------------------------------------------
+
+static u16
+shift_in_bits(struct e100_private *adapter)
+{
+ u16 x, d, i;
+
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ x &= ~(EEDO | EEDI);
+ d = 0;
+
+ for (i = 0; i < 16; i++) {
+ d <<= 1;
+ raise_clock(adapter, &x);
+
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ x &= ~EEDI;
+ if (x & EEDO)
+ d |= 1;
+
+ lower_clock(adapter, &x);
+ }
+
+ return d;
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: e100_eeprom_cleanup
+//
+// Description: This routine returns the EEPROM to an idle state
+//----------------------------------------------------------------------------------------
+
+void
+e100_eeprom_cleanup(struct e100_private *adapter)
+{
+ u16 x;
+
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ x &= ~(EECS | EEDI);
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ raise_clock(adapter, &x);
+ lower_clock(adapter, &x);
+}
+
+//**********************************************************************************
+// Procedure: e100_eeprom_update_chksum
+//
+// Description: Calculates the checksum and writes it to the EEProm.
+// It calculates the checksum accroding to the formula:
+// Checksum = 0xBABA - (sum of first 63 words).
+//
+//-----------------------------------------------------------------------------------
+u16
+e100_eeprom_calculate_chksum(struct e100_private *adapter)
+{
+ u16 idx, xsum_index, checksum = 0;
+
+ // eeprom size is initialized to zero
+ if (!adapter->eeprom_size)
+ adapter->eeprom_size = e100_eeprom_size(adapter);
+
+ xsum_index = adapter->eeprom_size - 1;
+ for (idx = 0; idx < xsum_index; idx++)
+ checksum += e100_eeprom_read(adapter, idx);
+
+ checksum = EEPROM_CHECKSUM - checksum;
+ return checksum;
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: e100_eeprom_write_word
+//
+// Description: This routine writes a word to a specific EEPROM location without.
+// taking EEPROM semaphore and updating checksum.
+// Use e100_eeprom_write_block for the EEPROM update
+// Arguments: reg - The EEPROM word that we are going to write to.
+// data - The data (word) that we are going to write to the EEPROM.
+//----------------------------------------------------------------------------------------
+static void
+e100_eeprom_write_word(struct e100_private *adapter, u16 reg, u16 data)
+{
+ u16 x;
+ u16 bits;
+
+ bits = eeprom_address_size(adapter->eeprom_size);
+
+ /* select EEPROM, mask off ASIC and reset bits, set EECS */
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ x &= ~(EEDI | EEDO | EESK);
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status)); /* flush command to card */
+ udelay(EEPROM_STALL_TIME);
+ x |= EECS;
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+
+ shift_out_bits(adapter, EEPROM_EWEN_OPCODE, 5);
+ shift_out_bits(adapter, reg, (u16) (bits - 2));
+ if (!eeprom_wait_cmd_done(adapter))
+ return;
+
+ /* write the new word to the EEPROM & send the write opcode the EEPORM */
+ shift_out_bits(adapter, EEPROM_WRITE_OPCODE, 3);
+
+ /* select which word in the EEPROM that we are writing to */
+ shift_out_bits(adapter, reg, bits);
+
+ /* write the data to the selected EEPROM word */
+ shift_out_bits(adapter, data, 16);
+ if (!eeprom_wait_cmd_done(adapter))
+ return;
+
+ shift_out_bits(adapter, EEPROM_EWDS_OPCODE, 5);
+ shift_out_bits(adapter, reg, (u16) (bits - 2));
+ if (!eeprom_wait_cmd_done(adapter))
+ return;
+
+ e100_eeprom_cleanup(adapter);
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: e100_eeprom_write_block
+//
+// Description: This routine writes a block of words starting from specified EEPROM
+// location and updates checksum
+// Arguments: reg - The EEPROM word that we are going to write to.
+// data - The data (word) that we are going to write to the EEPROM.
+//----------------------------------------------------------------------------------------
+void
+e100_eeprom_write_block(struct e100_private *adapter, u16 start, u16 *data,
+ u16 size)
+{
+ u16 checksum;
+ u16 i;
+
+ if (!adapter->eeprom_size)
+ adapter->eeprom_size = e100_eeprom_size(adapter);
+
+ // Set EEPROM semaphore.
+ if (adapter->rev_id >= D102_REV_ID) {
+ if (!eeprom_set_semaphore(adapter))
+ return;
+ }
+
+ for (i = 0; i < size; i++) {
+ e100_eeprom_write_word(adapter, start + i, data[i]);
+ }
+ //Update checksum
+ checksum = e100_eeprom_calculate_chksum(adapter);
+ e100_eeprom_write_word(adapter, (adapter->eeprom_size - 1), checksum);
+
+ // Clear EEPROM Semaphore.
+ if (adapter->rev_id >= D102_REV_ID) {
+ eeprom_reset_semaphore(adapter);
+ }
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: eeprom_wait_cmd_done
+//
+// Description: This routine waits for the the EEPROM to finish its command.
+// Specifically, it waits for EEDO (data out) to go high.
+// Returns: true - If the command finished
+// false - If the command never finished (EEDO stayed low)
+//----------------------------------------------------------------------------------------
+static u16
+eeprom_wait_cmd_done(struct e100_private *adapter)
+{
+ u16 x;
+ unsigned long expiration_time = jiffies + HZ / 100 + 1;
+
+ eeprom_stand_by(adapter);
+
+ do {
+ rmb();
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ if (x & EEDO)
+ return true;
+ if (time_before(jiffies, expiration_time))
+ yield();
+ else
+ return false;
+ } while (true);
+}
+
+//----------------------------------------------------------------------------------------
+// Procedure: eeprom_stand_by
+//
+// Description: This routine lowers the EEPROM chip select (EECS) for a few microseconds.
+//----------------------------------------------------------------------------------------
+static void
+eeprom_stand_by(struct e100_private *adapter)
+{
+ u16 x;
+
+ x = readw(&CSR_EEPROM_CONTROL_FIELD(adapter));
+ x &= ~(EECS | EESK);
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status)); /* flush command to card */
+ udelay(EEPROM_STALL_TIME);
+ x |= EECS;
+ writew(x, &CSR_EEPROM_CONTROL_FIELD(adapter));
+ readw(&(adapter->scb->scb_status)); /* flush command to card */
+ udelay(EEPROM_STALL_TIME);
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)