LAN9118 improvements
Implement LAN9118 general purpose timer and PHY interrupts. Fix global interrupt status bit. Signed-off-by: Paul Brook <paul@codesourcery.com>
This commit is contained in:
parent
73b01960b4
commit
209bf9658a
94
hw/lan9118.c
94
hw/lan9118.c
@ -40,8 +40,8 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
|
||||
#define CSR_TX_FIFO_INF 0x80
|
||||
#define CSR_PMT_CTRL 0x84
|
||||
#define CSR_GPIO_CFG 0x88
|
||||
#define CSR_GPT_CFG 0x8c /* TODO */
|
||||
#define CSR_GPT_CNT 0x90 /* TODO */
|
||||
#define CSR_GPT_CFG 0x8c
|
||||
#define CSR_GPT_CNT 0x90
|
||||
#define CSR_WORD_SWAP 0x98
|
||||
#define CSR_FREE_RUN 0x9c
|
||||
#define CSR_RX_DROP 0xa0
|
||||
@ -52,6 +52,7 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
|
||||
#define CSR_E2P_DATA 0xb4
|
||||
|
||||
/* IRQ_CFG */
|
||||
#define IRQ_INT 0x00001000
|
||||
#define IRQ_EN 0x00000100
|
||||
#define IRQ_POL 0x00000010
|
||||
#define IRQ_TYPE 0x00000001
|
||||
@ -117,6 +118,16 @@ do { fprintf(stderr, "lan9118: error: " fmt , ## __VA_ARGS__);} while (0)
|
||||
#define MAC_CR_RXEN 0x00000004
|
||||
#define MAC_CR_RESERVED 0x7f404213
|
||||
|
||||
#define PHY_INT_ENERGYON 0x80
|
||||
#define PHY_INT_AUTONEG_COMPLETE 0x40
|
||||
#define PHY_INT_FAULT 0x20
|
||||
#define PHY_INT_DOWN 0x10
|
||||
#define PHY_INT_AUTONEG_LP 0x08
|
||||
#define PHY_INT_PARFAULT 0x04
|
||||
#define PHY_INT_AUTONEG_PAGE 0x02
|
||||
|
||||
#define GPT_TIMER_EN 0x20000000
|
||||
|
||||
enum tx_state {
|
||||
TX_IDLE,
|
||||
TX_B,
|
||||
@ -141,6 +152,7 @@ typedef struct {
|
||||
NICConf conf;
|
||||
qemu_irq irq;
|
||||
int mmio_index;
|
||||
ptimer_state *timer;
|
||||
|
||||
uint32_t irq_cfg;
|
||||
uint32_t int_sts;
|
||||
@ -151,6 +163,7 @@ typedef struct {
|
||||
uint32_t hw_cfg;
|
||||
uint32_t pmt_ctrl;
|
||||
uint32_t gpio_cfg;
|
||||
uint32_t gpt_cfg;
|
||||
uint32_t word_swap;
|
||||
uint32_t free_timer_start;
|
||||
uint32_t mac_cmd;
|
||||
@ -169,6 +182,8 @@ typedef struct {
|
||||
uint32_t phy_status;
|
||||
uint32_t phy_control;
|
||||
uint32_t phy_advertise;
|
||||
uint32_t phy_int;
|
||||
uint32_t phy_int_mask;
|
||||
|
||||
int eeprom_writable;
|
||||
uint8_t eeprom[8];
|
||||
@ -204,6 +219,11 @@ static void lan9118_update(lan9118_state *s)
|
||||
|
||||
/* TODO: Implement FIFO level IRQs. */
|
||||
level = (s->int_sts & s->int_en) != 0;
|
||||
if (level) {
|
||||
s->irq_cfg |= IRQ_INT;
|
||||
} else {
|
||||
s->irq_cfg &= ~IRQ_INT;
|
||||
}
|
||||
if ((s->irq_cfg & IRQ_EN) == 0) {
|
||||
level = 0;
|
||||
}
|
||||
@ -231,14 +251,28 @@ static void lan9118_reload_eeprom(lan9118_state *s)
|
||||
lan9118_mac_changed(s);
|
||||
}
|
||||
|
||||
static void phy_update_irq(lan9118_state *s)
|
||||
{
|
||||
if (s->phy_int & s->phy_int_mask) {
|
||||
s->int_sts |= PHY_INT;
|
||||
} else {
|
||||
s->int_sts &= ~PHY_INT;
|
||||
}
|
||||
lan9118_update(s);
|
||||
}
|
||||
|
||||
static void phy_update_link(lan9118_state *s)
|
||||
{
|
||||
/* Autonegotiation status mirrors link status. */
|
||||
if (s->nic->nc.link_down) {
|
||||
s->phy_status &= ~0x0024;
|
||||
s->phy_int |= PHY_INT_DOWN;
|
||||
} else {
|
||||
s->phy_status |= 0x0024;
|
||||
s->phy_int |= PHY_INT_ENERGYON;
|
||||
s->phy_int |= PHY_INT_AUTONEG_COMPLETE;
|
||||
}
|
||||
phy_update_irq(s);
|
||||
}
|
||||
|
||||
static void lan9118_set_link(VLANClientState *nc)
|
||||
@ -248,9 +282,11 @@ static void lan9118_set_link(VLANClientState *nc)
|
||||
|
||||
static void phy_reset(lan9118_state *s)
|
||||
{
|
||||
s->phy_status = 0x7805;
|
||||
s->phy_status = 0x7809;
|
||||
s->phy_control = 0x3000;
|
||||
s->phy_advertise = 0x01e1;
|
||||
s->phy_int_mask = 0;
|
||||
s->phy_int = 0;
|
||||
phy_update_link(s);
|
||||
}
|
||||
|
||||
@ -292,6 +328,10 @@ static void lan9118_reset(DeviceState *d)
|
||||
s->e2p_data = 0;
|
||||
s->free_timer_start = qemu_get_clock(vm_clock) / 40;
|
||||
|
||||
ptimer_stop(s->timer);
|
||||
ptimer_set_count(s->timer, 0xffff);
|
||||
s->gpt_cfg = 0xffff;
|
||||
|
||||
s->mac_cr = MAC_CR_PRMS;
|
||||
s->mac_hashh = 0;
|
||||
s->mac_hashl = 0;
|
||||
@ -640,6 +680,8 @@ static void tx_fifo_push(lan9118_state *s, uint32_t val)
|
||||
|
||||
static uint32_t do_phy_read(lan9118_state *s, int reg)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
switch (reg) {
|
||||
case 0: /* Basic Control */
|
||||
return s->phy_control;
|
||||
@ -656,6 +698,13 @@ static uint32_t do_phy_read(lan9118_state *s, int reg)
|
||||
case 6: /* Auto-neg Expansion */
|
||||
return 1;
|
||||
/* TODO 17, 18, 27, 29, 30, 31 */
|
||||
case 29: /* Interrupt source. */
|
||||
val = s->phy_int;
|
||||
s->phy_int = 0;
|
||||
phy_update_irq(s);
|
||||
return val;
|
||||
case 30: /* Interrupt mask */
|
||||
return s->phy_int_mask;
|
||||
default:
|
||||
BADF("PHY read reg %d\n", reg);
|
||||
return 0;
|
||||
@ -679,7 +728,11 @@ static void do_phy_write(lan9118_state *s, int reg, uint32_t val)
|
||||
case 4: /* Auto-neg advertisment */
|
||||
s->phy_advertise = (val & 0x2d7f) | 0x80;
|
||||
break;
|
||||
/* TODO 17, 18, 27, 29, 30, 31 */
|
||||
/* TODO 17, 18, 27, 31 */
|
||||
case 30: /* Interrupt mask */
|
||||
s->phy_int_mask = val & 0xff;
|
||||
phy_update_irq(s);
|
||||
break;
|
||||
default:
|
||||
BADF("PHY write reg %d = 0x%04x\n", reg, val);
|
||||
}
|
||||
@ -820,6 +873,15 @@ static void lan9118_eeprom_cmd(lan9118_state *s, int cmd, int addr)
|
||||
}
|
||||
}
|
||||
|
||||
static void lan9118_tick(void *opaque)
|
||||
{
|
||||
lan9118_state *s = (lan9118_state *)opaque;
|
||||
if (s->int_en & GPT_INT) {
|
||||
s->int_sts |= GPT_INT;
|
||||
}
|
||||
lan9118_update(s);
|
||||
}
|
||||
|
||||
static void lan9118_writel(void *opaque, target_phys_addr_t offset,
|
||||
uint32_t val)
|
||||
{
|
||||
@ -835,7 +897,7 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
|
||||
switch (offset) {
|
||||
case CSR_IRQ_CFG:
|
||||
/* TODO: Implement interrupt deassertion intervals. */
|
||||
s->irq_cfg = (val & IRQ_EN);
|
||||
s->irq_cfg = (s->irq_cfg & IRQ_INT) | (val & IRQ_EN);
|
||||
break;
|
||||
case CSR_INT_STS:
|
||||
s->int_sts &= ~val;
|
||||
@ -905,6 +967,18 @@ static void lan9118_writel(void *opaque, target_phys_addr_t offset,
|
||||
/* Probably just enabling LEDs. */
|
||||
s->gpio_cfg = val & 0x7777071f;
|
||||
break;
|
||||
case CSR_GPT_CFG:
|
||||
if ((s->gpt_cfg ^ val) & GPT_TIMER_EN) {
|
||||
if (val & GPT_TIMER_EN) {
|
||||
ptimer_set_count(s->timer, val & 0xffff);
|
||||
ptimer_run(s->timer, 0);
|
||||
} else {
|
||||
ptimer_stop(s->timer);
|
||||
ptimer_set_count(s->timer, 0xffff);
|
||||
}
|
||||
}
|
||||
s->gpt_cfg = val & (GPT_TIMER_EN | 0xffff);
|
||||
break;
|
||||
case CSR_WORD_SWAP:
|
||||
/* Ignored because we're in 32-bit mode. */
|
||||
s->word_swap = val;
|
||||
@ -988,6 +1062,10 @@ static uint32_t lan9118_readl(void *opaque, target_phys_addr_t offset)
|
||||
return s->pmt_ctrl;
|
||||
case CSR_GPIO_CFG:
|
||||
return s->gpio_cfg;
|
||||
case CSR_GPT_CFG:
|
||||
return s->gpt_cfg;
|
||||
case CSR_GPT_CNT:
|
||||
return ptimer_get_count(s->timer);
|
||||
case CSR_WORD_SWAP:
|
||||
return s->word_swap;
|
||||
case CSR_FREE_RUN:
|
||||
@ -1041,6 +1119,7 @@ static NetClientInfo net_lan9118_info = {
|
||||
static int lan9118_init1(SysBusDevice *dev)
|
||||
{
|
||||
lan9118_state *s = FROM_SYSBUS(lan9118_state, dev);
|
||||
QEMUBH *bh;
|
||||
int i;
|
||||
|
||||
s->mmio_index = cpu_register_io_memory(lan9118_readfn,
|
||||
@ -1059,6 +1138,11 @@ static int lan9118_init1(SysBusDevice *dev)
|
||||
s->pmt_ctrl = 1;
|
||||
s->txp = &s->tx_packet;
|
||||
|
||||
bh = qemu_bh_new(lan9118_tick, s);
|
||||
s->timer = ptimer_init(bh);
|
||||
ptimer_set_freq(s->timer, 10000);
|
||||
ptimer_set_limit(s->timer, 0xffff, 1);
|
||||
|
||||
/* ??? Save/restore. */
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user