|
@@ -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,
|