Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a4bdfff7 authored by Sebastien Jan's avatar Sebastien Jan Committed by David S. Miller
Browse files

ks8851: Low level functions for read/write to companion eeprom



Low-level functions provide 16bits words read and write capability
to ks8851 companion eeprom.

Signed-off-by: default avatarSebastien Jan <s-jan@ti.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 7d997466
Loading
Loading
Loading
Loading
+228 −0
Original line number Diff line number Diff line
@@ -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,
+13 −1
Original line number Diff line number Diff line
@@ -25,12 +25,24 @@
#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)