Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | // 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); } |