| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * pic32_mdio.c: PIC32 MDIO/MII driver, part of pic32_eth.c. |
| * |
| * Copyright 2015 Microchip Inc. |
| * Purna Chandra Mandal <purna.mandal@microchip.com> |
| */ |
| #include <phy.h> |
| #include <miiphy.h> |
| #include <errno.h> |
| #include <wait_bit.h> |
| #include <asm/io.h> |
| #include <linux/delay.h> |
| #include "pic32_eth.h" |
| |
| static int pic32_mdio_write(struct mii_dev *bus, |
| int addr, int dev_addr, |
| int reg, u16 value) |
| { |
| u32 v; |
| struct pic32_mii_regs *mii_regs = bus->priv; |
| |
| /* Wait for the previous operation to finish */ |
| wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, true); |
| |
| /* Put phyaddr and regaddr into MIIMADD */ |
| v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); |
| writel(v, &mii_regs->madr.raw); |
| |
| /* Initiate a write command */ |
| writel(value, &mii_regs->mwtd.raw); |
| |
| /* Wait 30 clock cycles for busy flag to be set */ |
| udelay(12); |
| |
| /* Wait for write to complete */ |
| wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, true); |
| |
| return 0; |
| } |
| |
| static int pic32_mdio_read(struct mii_dev *bus, int addr, int devaddr, int reg) |
| { |
| u32 v; |
| struct pic32_mii_regs *mii_regs = bus->priv; |
| |
| /* Wait for the previous operation to finish */ |
| wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, true); |
| |
| /* Put phyaddr and regaddr into MIIMADD */ |
| v = (addr << MIIMADD_PHYADDR_SHIFT) | (reg & MIIMADD_REGADDR); |
| writel(v, &mii_regs->madr.raw); |
| |
| /* Initiate a read command */ |
| writel(MIIMCMD_READ, &mii_regs->mcmd.raw); |
| |
| /* Wait 30 clock cycles for busy flag to be set */ |
| udelay(12); |
| |
| /* Wait for read to complete */ |
| wait_for_bit_le32(&mii_regs->mind.raw, |
| MIIMIND_NOTVALID | MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, false); |
| |
| /* Clear the command register */ |
| writel(0, &mii_regs->mcmd.raw); |
| |
| /* Grab the value read from the PHY */ |
| v = readl(&mii_regs->mrdd.raw); |
| return v; |
| } |
| |
| static int pic32_mdio_reset(struct mii_dev *bus) |
| { |
| struct pic32_mii_regs *mii_regs = bus->priv; |
| |
| /* Reset MII (due to new addresses) */ |
| writel(MIIMCFG_RSTMGMT, &mii_regs->mcfg.raw); |
| |
| /* Wait for the operation to finish */ |
| wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, true); |
| |
| /* Clear reset bit */ |
| writel(0, &mii_regs->mcfg); |
| |
| /* Wait for the operation to finish */ |
| wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, true); |
| |
| /* Set the MII Management Clock (MDC) - no faster than 2.5 MHz */ |
| writel(MIIMCFG_CLKSEL_DIV40, &mii_regs->mcfg.raw); |
| |
| /* Wait for the operation to finish */ |
| wait_for_bit_le32(&mii_regs->mind.raw, MIIMIND_BUSY, |
| false, CONFIG_SYS_HZ, true); |
| return 0; |
| } |
| |
| int pic32_mdio_init(const char *name, ulong ioaddr) |
| { |
| struct mii_dev *bus; |
| |
| bus = mdio_alloc(); |
| if (!bus) { |
| printf("Failed to allocate PIC32-MDIO bus\n"); |
| return -ENOMEM; |
| } |
| |
| bus->read = pic32_mdio_read; |
| bus->write = pic32_mdio_write; |
| bus->reset = pic32_mdio_reset; |
| strncpy(bus->name, name, sizeof(bus->name)); |
| bus->priv = (void *)ioaddr; |
| |
| return mdio_register(bus); |
| } |