.dts para driver Ethernet

#1

Oi pessoal e devs,

Estamos avançando bem na refatoração do driver de ethernet, seguindo a arquitetura do driver da nxp (lpc_eth.c), como sugerido por Edgar.

No momento estamos com dificuldades em ligar o PHY da placa e fazer com que ele responda no bus. O nosso driver dá probe normalmente mas parece não achar o PHY. Nos parece que é necessário modificar o .dts para refletir a nova arquitetura. Nem mesmo os LEDs do PHY estão acesos. Será que alguém pode nos ajudar? Obrigado! Segue a parte do log do dmesg que me parece relevante:



#2

olá, @brunocarneirodacunha

Me parece haver um problema do gpio não conseguir pegar o GPIO reset, isso pela mensagem;

lookup for GPIO reset failed.

Já verificou se as condições de hardware estão corretas? qual nível de tensão no pino de reset do chip?

Se estiver tudo correto, por que desta mensagem?

@brunocarneirodacunha, por favor evite de colocar esses logs em formato de imagem, coloque o texto do log como citação ou ao menos em itálico.

Att,

Igor Ruschi

#3

Então, mesmo setando o reset e o power no GPIO (do mesmo jeito que era feito no driver anterior) ainda não temos o 3.3v neles, então parece ter algum problema no módulo GPIO.

Aqui está nosso repositório, caso vocês queiram dar uma olhada.

#4

Eu testei também aqui, o resultado foi o mesmo que você citou, então alterei algumas funções da api de gpio e também não deu certo, então externei as gpios para um driver em modulo separado;

#include #include #include #include #include

typedef struct {
int phy_reset_gpio;
int phy_power_gpio;
}phy_gpio;

static phy_gpio *global_data = NULL;

void eth_turn_on(void)
{
phy_gpio *data = global_data;

if (data)
{
	gpio_set_value(data->phy_power_gpio, 1);

	mdelay(150);//time for power up

	gpio_set_value(data->phy_reset_gpio, 1);
	mdelay(12);
	gpio_set_value(data->phy_reset_gpio, 0);
	mdelay(12);
	gpio_set_value(data->phy_reset_gpio, 1);
	mdelay(150);
	pr_info("phy module turned on\n");
}

}
EXPORT_SYMBOL(eth_turn_on);

void eth_turn_off(void)
{
phy_gpio *data = global_data;

if (data)
{
	gpio_set_value(data->phy_reset_gpio, 0);

	gpio_set_value(data->phy_power_gpio, 0);

	mdelay(15);
	
	pr_info("phy module turned off\n");
}

}
EXPORT_SYMBOL(eth_turn_off);

static int __init eth_power_probe(struct platform_device * pdev)
{
phy_gpio *data = NULL;
struct device *dev = &pdev->dev;
int ret;

platform_set_drvdata(pdev, NULL);

data = devm_kzalloc(dev, sizeof(phy_gpio), GFP_KERNEL);

if (!data)
{
	dev_err(dev, "could not alloc memory\n");
	return -ENOMEM;
}

data->phy_power_gpio = 
	of_get_named_gpio(dev->of_node, "phy-power-gpio", 0);

if (!gpio_is_valid(data->phy_power_gpio))
{
	dev_err(dev, "could not get phy power gpio\n");
	return -ENODEV;
}

data->phy_reset_gpio = 
	of_get_named_gpio(dev->of_node, "phy-reset-gpio", 0);

if (!gpio_is_valid(data->phy_reset_gpio))
{
	dev_err(dev, "could not get phy reset gpio\n");
	return -ENODEV;
}

ret = devm_gpio_request(dev, data->phy_power_gpio, "phy_power");

if (ret)
{
	dev_err(dev, "could not request wifi power gpio\n");
	return ret;
}

ret = devm_gpio_request(dev, data->phy_reset_gpio, "phy_reset");

if (ret)
{
	dev_err(dev, "could not request phy reset gpio\n");
	return ret;
}

gpio_direction_output(data->phy_power_gpio, 0);

gpio_direction_output(data->phy_reset_gpio, 0);

mdelay(20);

global_data = data;

platform_set_drvdata(pdev, data);

eth_turn_on();

dev_info(dev, "eth power probe ok\n");

return 0;

}

static int __exit eth_power_remove(struct platform_device *pdev)
{
phy_gpio *data = platform_get_drvdata(pdev);

if (data)
{
	eth_turn_off();
	
	global_data = NULL;
	
	dev_info(&pdev->dev, "driver remove ok\n");
}

return 0;

}

static const struct of_device_id eth_power_dt_match[] = {
{.compatible = “caninos,labrador-eth-power”},
{}
};

MODULE_DEVICE_TABLE(of, eth_power_dt_match);

static struct platform_driver __refdata eth_power_driver = {
.probe = eth_power_probe,
.remove = eth_power_remove,
.driver = {
.name = “labrador-eth-power”,
.owner = THIS_MODULE,
.of_match_table = eth_power_dt_match,
},
};

static int __init eth_power_init(void)
{
return platform_driver_register(&eth_power_driver);
}

//device_initcall(eth_power_init);
module_platform_driver(eth_power_driver);
MODULE_LICENSE(“GPL”);

também não funcionou, vou olhar o datasheet e ver se tem algo multiplexado com esses pinos…

att,

Igor Ruschi

#5

Achei o problema, esse GPIOB11 tem uma função analógica, é necessário ativar ele como digital, no código antigo ele era alterado na função ethernet_set_pin_mux, neste trecho de código;

//setting the mux of GPIOB11/OEN to digital, otherwise GPIO will not work

  • temp = act_readl(MFP_CTL1);*
  • temp &= ~(0x3<<21);//mask*
  • temp |= (0x2<<21);//set bits[22:21] = 0b10*
  • act_writel(temp, MFP_CTL1);*

att,

Igor Ruschi

#6

Olá, mais uma coisa que percebi é que o driver de PHY para realtek para o linux 4.14 (/driver/net/phy/realtec.c) não suporta o nosso PHY rtl8201F, tem que adicionar ele, mas algo ainda não está certo ele continua a identificar o phy_id como 0xfffff, daí ele usa o driver de PHY genérico mesmo depois de adicionar o driver de phy correto.

Tem que ver como está a comunicação entre o phy e o mac, parece haver alguma leitura errada do phy_id.

#7

Oi Igor! Funcionou essa parte! Muito obrigado!

#8

Achamos o erro que faltava, Igor!

Era uma besteira, estávamos lendo o registrador e operando com & em cima do valor retornado. Só que isso fazia com que o OPCODE do MAC_CSR10 ficasse sempre no CLOCKSET. Enfim, estamos lendo o ID do PHY corretamente. Muito obrigado pela ajuda!

#9

@brunocarneirodacunha, que ótima noticia, que bom que conseguimos ajudar!!

:+1:

Acho que com isso o driver do phy deve estar sendo chamado, isso também ficou ok?

#10

Oi @ruschigo!
Estamos caminhando aqui com o driver. Queria te pedir mais uma ajuda:

O controlador MAC parece não conseguir acessar o buffer de dados dos descritores. Ele lê o resto do descritor normalmente, mas na hora de fazer uma transferência para a FIFO interna ele simplesmente retorna imediatamente um TU (transmit buffer unavailable) e zera o bit do Ownership daquele descritor, como se o buffer de dados estivesse vazio.

Acompanhando o estado do registrador de Status, posso ver que ele nem mesmo começa a transferência. Vai direto do estado “Running, fetching transmit descriptor” para o “Transmit buffer unavailable”.

Você sabe o que pode estar acontecendo? Segue aqui embaixo nosso código do ndo_start_xmit:
Obrigado!

static int
labrador_eth_hard_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
struct netdata_local *pldat = netdev_priv(ndev);
struct txrx_desc_t *ptxrxdesc;
int i = 0;
INFO_MSG(“labrador_eth_hard_start_xmit”);

spin_lock_irq(&pldat->lock);

if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1)) {
    /* This function should never be called when there are no
       buffers */
    netif_stop_queue(ndev);
    spin_unlock_irq(&pldat->lock);
    WARN(1, "BUG! TX request when no free TX buffers!\n");
    return NETDEV_TX_BUSY;
} 

/* Setup control for the transfer */
ptxrxdesc = &pldat->tx_desc_v[pldat->txidx];
ptxrxdesc->control = TX_DESC_CTRL_TBS1(skb->len);
ptxrxdesc->control |= TX_DESC_CTRL_FS | TX_DESC_CTRL_LS | TX_DESC_CTRL_IC;
mb();
ptxrxdesc->status = TX_DESC_STAT_OWN;
mb();

/* Copy data to the DMA buffer */
memcpy(pldat->tx_buff_v + pldat->txidx * ENET_MAXF_SIZE, skb->data, skb->len);

/* Save the buffer and increment the buffer counter */
pldat->skblen[pldat->txidx] = skb->len;
pldat->num_used_tx_buffs++;

pldat->txidx++;
if (pldat->txidx >= ENET_TX_DESC) {
    pldat->txidx = 0;
    ptxrxdesc->control |= TX_DESC_CTRL_TER;
}

/* Start transmit */
writel(LAB_TRANSMIT_ENABLE, LAB_ENET_TRANSMIT(pldat->net_base));

/* Stop queue if no more TX buffers */
if (pldat->num_used_tx_buffs >= (ENET_TX_DESC - 1))
    netif_stop_queue(ndev);

out:
dev_kfree_skb(skb);
spin_unlock_irq(&pldat->lock);

return NETDEV_TX_OK;

}