#include "dmfe_impl.h"
static const int mii_reg_size = 16;
void
dmfe_read_eeprom(dmfe_t *dmfep, uint16_t raddr, uint8_t *ptr, int cnt)
{
uint16_t value;
uint16_t bit;
ASSERT((cnt % 2) == 0);
ASSERT((raddr % 2) == 0);
ASSERT(cnt > 0);
ASSERT(((raddr + cnt) / 2) < (HIGH_ADDRESS_BIT << 1));
raddr /= 2;
while (cnt > 0) {
dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS | SEL_CLK);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
drv_usecwait(1);
for (bit = HIGH_CMD_BIT; bit != 0; bit >>= 1) {
value = (bit & EEPROM_READ_CMD) ? DATA_IN : 0;
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | value);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | SEL_CLK | value);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | value);
drv_usecwait(1);
}
for (bit = HIGH_ADDRESS_BIT; bit != 0; bit >>= 1) {
value = (bit & raddr) ? DATA_IN : 0;
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | value);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | SEL_CLK | value);
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | value);
drv_usecwait(1);
}
value = 0;
for (bit = HIGH_DATA_BIT; bit != 0; bit >>= 1) {
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
READ_EEPROM_CS | SEL_CLK);
drv_usecwait(1);
if (dmfe_chip_get32(dmfep, ETHER_ROM_REG) & DATA_OUT)
value |= bit;
drv_usecwait(1);
dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM_CS);
drv_usecwait(1);
}
dmfe_chip_put32(dmfep, ETHER_ROM_REG, READ_EEPROM);
drv_usecwait(1);
*ptr++ = value & 0xff;
*ptr++ = (value >> 8);
cnt -= 2;
raddr++;
}
}
static void
dmfe_poke_mii(dmfe_t *dmfep, uint32_t mii_data, uint_t nbits)
{
uint32_t dbit;
ASSERT(mutex_owned(dmfep->milock));
for (; nbits > 0; mii_data <<= 1, --nbits) {
dbit = mii_data >> 31;
dbit <<= MII_DATA_OUT_SHIFT;
ASSERT((dbit & ~MII_DATA_OUT) == 0);
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
MII_WRITE | dbit);
drv_usecwait(MII_DELAY);
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
MII_WRITE | MII_CLOCK | dbit);
drv_usecwait(MII_DELAY);
}
dmfe_chip_put32(dmfep, ETHER_ROM_REG,
MII_WRITE | dbit);
drv_usecwait(MII_DELAY);
}
static void
dmfe_tristate_mii(dmfe_t *dmfep)
{
ASSERT(mutex_owned(dmfep->milock));
dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_TRISTATE);
drv_usecwait(MII_DELAY);
dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_TRISTATE | MII_CLOCK);
drv_usecwait(MII_DELAY);
}
static void
dmfe_mii_command(dmfe_t *dmfep, uint32_t command_word, int nbits)
{
ASSERT(mutex_owned(dmfep->milock));
dmfe_poke_mii(dmfep, MII_PREAMBLE, 2*mii_reg_size);
dmfe_poke_mii(dmfep, command_word, nbits);
dmfe_tristate_mii(dmfep);
}
static uint16_t
dmfe_mii_response(dmfe_t *dmfep)
{
boolean_t ack;
uint16_t data;
uint32_t tmp;
int i;
tmp = dmfe_chip_get32(dmfep, ETHER_ROM_REG);
ack = (tmp & MII_DATA_IN) == 0;
for (data = 0, i = 0; i < mii_reg_size; ++i) {
dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_READ);
drv_usecwait(MII_DELAY);
dmfe_chip_put32(dmfep, ETHER_ROM_REG, MII_READ | MII_CLOCK);
drv_usecwait(MII_DELAY);
tmp = dmfe_chip_get32(dmfep, ETHER_ROM_REG);
data <<= 1;
data |= (tmp >> MII_DATA_IN_SHIFT) & 1;
}
dmfe_tristate_mii(dmfep);
return (ack ? data : ~0);
}
static void
dmfe_mii_write(void *arg, uint8_t phy_num, uint8_t reg_num, uint16_t reg_dat)
{
dmfe_t *dmfep = arg;
uint32_t command_word;
mutex_enter(dmfep->milock);
command_word = MII_WRITE_FRAME;
command_word |= phy_num << MII_PHY_ADDR_SHIFT;
command_word |= reg_num << MII_REG_ADDR_SHIFT;
command_word |= reg_dat;
dmfe_mii_command(dmfep, command_word, 2*mii_reg_size);
mutex_exit(dmfep->milock);
}
static uint16_t
dmfe_mii_read(void *arg, uint8_t phy_num, uint8_t reg_num)
{
dmfe_t *dmfep = arg;
uint32_t command_word;
uint16_t rv;
command_word = MII_READ_FRAME;
command_word |= phy_num << MII_PHY_ADDR_SHIFT;
command_word |= reg_num << MII_REG_ADDR_SHIFT;
mutex_enter(dmfep->milock);
dmfe_mii_command(dmfep, command_word, mii_reg_size-2);
rv = dmfe_mii_response(dmfep);
mutex_exit(dmfep->milock);
return (rv);
}
static void
dmfe_mii_notify(void *arg, link_state_t link)
{
dmfe_t *dmfep = arg;
if (link == LINK_STATE_UP) {
mutex_enter(dmfep->oplock);
if (mii_get_duplex(dmfep->mii) == LINK_DUPLEX_FULL) {
dmfep->opmode |= FULL_DUPLEX;
} else {
dmfep->opmode &= ~FULL_DUPLEX;
}
dmfe_chip_put32(dmfep, OPN_MODE_REG, dmfep->opmode);
mutex_exit(dmfep->oplock);
}
mac_link_update(dmfep->mh, link);
}
static mii_ops_t dmfe_mii_ops = {
MII_OPS_VERSION,
dmfe_mii_read,
dmfe_mii_write,
dmfe_mii_notify,
NULL,
};
boolean_t
dmfe_init_phy(dmfe_t *dmfep)
{
dmfep->mii = mii_alloc(dmfep, dmfep->devinfo, &dmfe_mii_ops);
if (dmfep->mii == NULL) {
return (B_FALSE);
}
return (B_TRUE);
}