]> git.karo-electronics.de Git - linux-beck.git/commitdiff
ks8851: Low level functions for read/write to companion eeprom
authorSebastien Jan <s-jan@ti.com>
Wed, 5 May 2010 08:45:53 +0000 (08:45 +0000)
committerDavid S. Miller <davem@davemloft.net>
Thu, 6 May 2010 04:27:44 +0000 (21:27 -0700)
Low-level functions provide 16bits words read and write capability
to ks8851 companion eeprom.

Signed-off-by: Sebastien Jan <s-jan@ti.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ks8851.c
drivers/net/ks8851.h

index 623171f37d999519f83dc64d796aff14bc316ced..3dba57b72ef871bb497a2067a991072c83d16ef2 100644 (file)
@@ -1034,6 +1034,234 @@ static const struct net_device_ops ks8851_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
+/* Companion eeprom access */
+
+enum { /* EEPROM programming states */
+       EEPROM_CONTROL,
+       EEPROM_ADDRESS,
+       EEPROM_DATA,
+       EEPROM_COMPLETE
+};
+
+/**
+ * ks8851_eeprom_read - read a 16bits word in ks8851 companion EEPROM
+ * @dev: The network device the PHY is on.
+ * @addr: EEPROM address to read
+ *
+ * eeprom_size: used to define the data coding length. Can be changed
+ * through debug-fs.
+ *
+ * Programs a read on the EEPROM using ks8851 EEPROM SW access feature.
+ * Warning: The READ feature is not supported on ks8851 revision 0.
+ *
+ * Rough programming model:
+ *  - on period start: set clock high and read value on bus
+ *  - on period / 2: set clock low and program value on bus
+ *  - start on period / 2
+ */
+unsigned int ks8851_eeprom_read(struct net_device *dev, unsigned int addr)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int eepcr;
+       int ctrl = EEPROM_OP_READ;
+       int state = EEPROM_CONTROL;
+       int bit_count = EEPROM_OP_LEN - 1;
+       unsigned int data = 0;
+       int dummy;
+       unsigned int addr_len;
+
+       addr_len = (ks->eeprom_size == 128) ? 6 : 8;
+
+       /* start transaction: chip select high, authorize write */
+       mutex_lock(&ks->lock);
+       eepcr = EEPCR_EESA | EEPCR_EESRWA;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr |= EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+       while (state != EEPROM_COMPLETE) {
+               /* falling clock period starts... */
+               /* set EED_IO pin for control and address */
+               eepcr &= ~EEPCR_EEDO;
+               switch (state) {
+               case EEPROM_CONTROL:
+                       eepcr |= ((ctrl >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0) {
+                               bit_count = addr_len - 1;
+                               state = EEPROM_ADDRESS;
+                       }
+                       break;
+               case EEPROM_ADDRESS:
+                       eepcr |= ((addr >> bit_count) & 1) << 2;
+                       bit_count--;
+                       break;
+               case EEPROM_DATA:
+                       /* Change to receive mode */
+                       eepcr &= ~EEPCR_EESRWA;
+                       break;
+               }
+
+               /* lower clock  */
+               eepcr &= ~EEPCR_EESCK;
+
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* waitread period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+
+               /* rising clock period starts... */
+
+               /* raise clock */
+               mutex_lock(&ks->lock);
+               eepcr |= EEPCR_EESCK;
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* Manage read */
+               switch (state) {
+               case EEPROM_ADDRESS:
+                       if (bit_count < 0) {
+                               bit_count = EEPROM_DATA_LEN - 1;
+                               state = EEPROM_DATA;
+                       }
+                       break;
+               case EEPROM_DATA:
+                       mutex_lock(&ks->lock);
+                       dummy = ks8851_rdreg16(ks, KS_EEPCR);
+                       mutex_unlock(&ks->lock);
+                       data |= ((dummy >> EEPCR_EESB_OFFSET) & 1) << bit_count;
+                       if (bit_count-- <= 0)
+                               state = EEPROM_COMPLETE;
+                       break;
+               }
+
+               /* wait period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+       }
+
+       /* close transaction */
+       mutex_lock(&ks->lock);
+       eepcr &= ~EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr = 0;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+       return data;
+}
+
+/**
+ * ks8851_eeprom_write - write a 16bits word in ks8851 companion EEPROM
+ * @dev: The network device the PHY is on.
+ * @op: operand (can be WRITE, EWEN, EWDS)
+ * @addr: EEPROM address to write
+ * @data: data to write
+ *
+ * eeprom_size: used to define the data coding length. Can be changed
+ * through debug-fs.
+ *
+ * Programs a write on the EEPROM using ks8851 EEPROM SW access feature.
+ *
+ * Note that a write enable is required before writing data.
+ *
+ * Rough programming model:
+ *  - on period start: set clock high
+ *  - on period / 2: set clock low and program value on bus
+ *  - start on period / 2
+ */
+void ks8851_eeprom_write(struct net_device *dev, unsigned int op,
+                                       unsigned int addr, unsigned int data)
+{
+       struct ks8851_net *ks = netdev_priv(dev);
+       int eepcr;
+       int state = EEPROM_CONTROL;
+       int bit_count = EEPROM_OP_LEN - 1;
+       unsigned int addr_len;
+
+       addr_len = (ks->eeprom_size == 128) ? 6 : 8;
+
+       switch (op) {
+       case EEPROM_OP_EWEN:
+               addr = 0x30;
+       break;
+       case EEPROM_OP_EWDS:
+               addr = 0;
+               break;
+       }
+
+       /* start transaction: chip select high, authorize write */
+       mutex_lock(&ks->lock);
+       eepcr = EEPCR_EESA | EEPCR_EESRWA;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr |= EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+       while (state != EEPROM_COMPLETE) {
+               /* falling clock period starts... */
+               /* set EED_IO pin for control and address */
+               eepcr &= ~EEPCR_EEDO;
+               switch (state) {
+               case EEPROM_CONTROL:
+                       eepcr |= ((op >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0) {
+                               bit_count = addr_len - 1;
+                               state = EEPROM_ADDRESS;
+                       }
+                       break;
+               case EEPROM_ADDRESS:
+                       eepcr |= ((addr >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0) {
+                               if (op == EEPROM_OP_WRITE) {
+                                       bit_count = EEPROM_DATA_LEN - 1;
+                                       state = EEPROM_DATA;
+                               } else {
+                                       state = EEPROM_COMPLETE;
+                               }
+                       }
+                       break;
+               case EEPROM_DATA:
+                       eepcr |= ((data >> bit_count) & 1) << 2;
+                       if (bit_count-- <= 0)
+                               state = EEPROM_COMPLETE;
+                       break;
+               }
+
+               /* lower clock  */
+               eepcr &= ~EEPCR_EESCK;
+
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* wait period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+
+               /* rising clock period starts... */
+
+               /* raise clock */
+               eepcr |= EEPCR_EESCK;
+               mutex_lock(&ks->lock);
+               ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+               mutex_unlock(&ks->lock);
+
+               /* wait period / 2 */
+               udelay(EEPROM_SK_PERIOD / 2);
+       }
+
+       /* close transaction */
+       mutex_lock(&ks->lock);
+       eepcr &= ~EEPCR_EECS;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       eepcr = 0;
+       ks8851_wrreg16(ks, KS_EEPCR, eepcr);
+       mutex_unlock(&ks->lock);
+
+}
+
 /* ethtool support */
 
 static void ks8851_get_drvinfo(struct net_device *dev,
index f52c312cc356c6deb2af0f2c9934d8a2a46cdd32..537fb06e593200144c8b1d09c1aae732416748e1 100644 (file)
 #define OBCR_ODS_16mA                          (1 << 6)
 
 #define KS_EEPCR                               0x22
+#define EEPCR_EESRWA                           (1 << 5)
 #define EEPCR_EESA                             (1 << 4)
-#define EEPCR_EESB                             (1 << 3)
+#define EEPCR_EESB_OFFSET                      3
+#define EEPCR_EESB                             (1 << EEPCR_EESB_OFFSET)
 #define EEPCR_EEDO                             (1 << 2)
 #define EEPCR_EESCK                            (1 << 1)
 #define EEPCR_EECS                             (1 << 0)
 
+#define EEPROM_OP_LEN                          3       /* bits:*/
+#define EEPROM_OP_READ                         0x06
+#define EEPROM_OP_EWEN                         0x04
+#define EEPROM_OP_WRITE                                0x05
+#define EEPROM_OP_EWDS                         0x14
+
+#define EEPROM_DATA_LEN                                16      /* 16 bits EEPROM */
+#define EEPROM_WRITE_TIME                      4       /* wrt ack time in ms */
+#define EEPROM_SK_PERIOD                       400     /* in us */
+
 #define KS_MBIR                                        0x24
 #define MBIR_TXMBF                             (1 << 12)
 #define MBIR_TXMBFA                            (1 << 11)