/** ******************************************************************************* * * Project: Robotics library for the Autonomous Robotics Development Platform * Port by: Jorge Sánchez de Nova jssdn (mail)_(at) kth.se * * NOTE: This code is based on the Xilinx XAPP1042 which, as noted by Xilinx, is based in Linux Kernel's * drivers/net/fs_enet by Vitaly Bordug and Pantelis Antoniou * * Description: * Software to access PHY registers in a system where the serial control bus * signals (MDC, MDIO) are connected to GPIO. * * Xilinx disclaimer below apply. * * XILINX IS PROVIDING THIS DESIGN, CODE, OR INFORMATION "AS IS" * SOLELY FOR USE IN DEVELOPING PROGRAMS AND SOLUTIONS FOR * XILINX DEVICES. BY PROVIDING THIS DESIGN, CODE, OR INFORMATION * AS ONE POSSIBLE IMPLEMENTATION OF THIS FEATURE, APPLICATION * OR STANDARD, XILINX IS MAKING NO REPRESENTATION THAT THIS * IMPLEMENTATION IS FREE FROM ANY CLAIMS OF INFRINGEMENT, * AND YOU ARE RESPONSIBLE FOR OBTAINING ANY RIGHTS YOU MAY REQUIRE * FOR YOUR IMPLEMENTATION. XILINX EXPRESSLY DISCLAIMS ANY * WARRANTY WHATSOEVER WITH RESPECT TO THE ADEQUACY OF THE * IMPLEMENTATION, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OR * REPRESENTATIONS THAT THIS IMPLEMENTATION IS FREE FROM CLAIMS OF * INFRINGEMENT, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE. * * (c) Copyright 2008 Xilinx, Inc. * All rights reserved. * ******************************************************************************* **/ #include #include #include #include #include "libsys.h" #include "gpio.h" #include "mii-bitbang.h" /* * mii_delay: * Pause a bit. */ static void mii_delay (void) { /* * Target MDC Frequency of 1/(2*1us) -- 500KHz */ usleep(1); } /* * mdio_mode_output: * set GPIO to OUTPUT */ static void mdio_mode_output(gpio_if_t *mii_gpio) { uint32_t dir; gpio_get_dir(mii_gpio, GPIO_0_MASK_MDIO, GPIO_0_ALIGN_MDIO, &dir); dir |= GPIO_0_MDIO_MDIO; /* Set the direction for the MDIO signal to be output */ gpio_set_dir(mii_gpio, GPIO_0_MASK_MDIO, GPIO_0_ALIGN_MDIO, dir); } /* * mdio_mode_input: * set GPIO to INPUT */ static void mdio_mode_input(gpio_if_t *mii_gpio) { uint32_t dir; gpio_get_dir(mii_gpio, GPIO_0_MASK_MDIO, GPIO_0_ALIGN_MDIO, &dir); dir &= ~GPIO_0_MDIO_MDIO; // printf("MDIO dir: 0x%x\n", dir); /* Set the direction for the MDIO signal to be input */ gpio_set_dir(mii_gpio, GPIO_0_MASK_MDIO, GPIO_0_ALIGN_MDIO, dir); } /* * mdio_read: * Read the value presently driven by the PHY on the MDIO GPIO */ static int mdio_read(gpio_if_t *mii_gpio) { uint32_t data; gpio_read_data(mii_gpio, GPIO_0_MASK_MDIO, GPIO_0_ALIGN_MDIO, &data); // printf("MDIO read: 0x%x\n", data); return (data & GPIO_0_MDIO_MDIO) != 0; } /* * mdio_drive_bit: * set the GPIO to drive the appropriate bit value on the MDIO pin */ static void mdio_drive_bit(gpio_if_t *mii_gpio, uint32_t value) { if (value) gpio_write(mii_gpio, GPIO_0_MDIO_MDIO, GPIO_0_ALIGN_MDIO, GPIO_DATA_OFFSET, ~0); else gpio_write(mii_gpio, GPIO_0_MDIO_MDIO, GPIO_0_ALIGN_MDIO, GPIO_DATA_OFFSET, 0); } /* * mdc_drive_bit: * Toggle the MDC GPIO to the appropriate bit. */ static void mdc_drive_bit(gpio_if_t *mii_gpio, uint32_t value) { if (value) gpio_write(mii_gpio, GPIO_0_MDIO_MDC, GPIO_0_ALIGN_MDIO, GPIO_DATA_OFFSET, ~0); else gpio_write(mii_gpio, GPIO_0_MDIO_MDC, GPIO_0_ALIGN_MDIO, GPIO_DATA_OFFSET, 0); } /* * mdc_clk_1_0: * Drive the MII Data Clock 1->0 */ static void mdc_clk_1_0 (gpio_if_t *mii_gpio) { mii_delay(); mdc_drive_bit(mii_gpio, 1); mii_delay(); mdc_drive_bit(mii_gpio, 0); } /* * mii_send_address: * Transmit the preamble, phy address, and phy register number on the bus. */ static void mii_send_address (gpio_if_t *mii_gpio, int read, uint8_t addr, uint8_t reg) { int i; /* * Send a 32 bit preamble of 1's */ mdio_mode_output(mii_gpio); mdio_drive_bit(mii_gpio, 1); /* <<<<< */ for (i = 0; i < MII_PREABLE_BITS; i++) { mdc_clk_1_0(mii_gpio); } /* * send the start bit (01) */ mdio_drive_bit(mii_gpio, 0); /* <<<<< */ mdc_clk_1_0(mii_gpio); mdio_drive_bit(mii_gpio, 1); /* <<<<< */ mdc_clk_1_0(mii_gpio); /* * send the opcode: read (10) write (10) */ mdio_drive_bit(mii_gpio, read); /* <<<<< */ mdc_clk_1_0(mii_gpio); mdio_drive_bit(mii_gpio, !read); /* <<<<< */ mdc_clk_1_0(mii_gpio); /* * send the PHY address */ for (i = 0; i < MII_5ADDRESS_BITS; i++) { if (addr & FIFTH_BIT_0x10) { mdio_drive_bit(mii_gpio, 1); /* <<<<< */ } else { mdio_drive_bit(mii_gpio, 0); /* <<<<< */ } mdc_clk_1_0(mii_gpio); addr <<= 1; } /* * send the register address */ for (i = 0; i < MII_5ADDRESS_BITS; i++) { if (reg & FIFTH_BIT_0x10) { mdio_drive_bit(mii_gpio, 1); /* <<<<< */ } else { mdio_drive_bit(mii_gpio, 0); /* <<<<< */ } mdc_clk_1_0(mii_gpio); reg <<= 1; } } /* * MiiGpio_PhyRead: * Read from an MII PHY register */ uint32_t MiiGpio_PhyRead (gpio_if_t *mii_gpio, uint32_t PhyAddress, uint32_t RegisterNum) { uint16_t regval; uint16_t i; /* * Bang the PHY address and PHY register on the bus. */ mii_send_address(mii_gpio, MII_READ, PhyAddress, RegisterNum); /* * Set GPIO mode to read */ mdio_mode_input(mii_gpio); mdc_clk_1_0(mii_gpio); /* * check the turnaround bit */ if (mdio_read(mii_gpio) != 0) { printf("ERROR: PHY not driving turnaround bit low.\n\r"); return -1; } /* * read 16 bits of register data, MSB first */ regval = 0; for (i = 0; i < MII_16REGISTER_BITS; i++) { mdc_clk_1_0(mii_gpio); regval <<= 1; regval |= mdio_read(mii_gpio); } mdc_clk_1_0(mii_gpio); return regval; } /* * MiiGpio_PhyWrite: * Write to an MII PHY register */ void MiiGpio_PhyWrite (gpio_if_t *mii_gpio, uint32_t PhyAddress, uint32_t RegisterNum, uint16_t PhyData) { int i; /* * Bang the PHY address and PHY register on the bus. */ mii_send_address(mii_gpio, MII_WRITE, PhyAddress, RegisterNum); /* send the turnaround (10) */ mdio_drive_bit(mii_gpio, 1); /* <<<<< */ mdc_clk_1_0(mii_gpio); mdio_drive_bit(mii_gpio, 0); /* <<<<< */ mdc_clk_1_0(mii_gpio); /* * write 16 bits of register data, MSB first */ for (i = 0; i < MII_16REGISTER_BITS; i++) { if (PhyData & MSB_16BITS_0x8000) { mdio_drive_bit(mii_gpio, 1); /* <<<<< */ } else { mdio_drive_bit(mii_gpio, 0); /* <<<<< */ } mdc_clk_1_0(mii_gpio); PhyData <<= 1; } mdio_mode_input(mii_gpio); mdc_clk_1_0(mii_gpio); } // TODO! /* * MiiGpio_Init: * Initialize GPIOs connected to PHY MDC and MDIO pins */ int MiiGpio_Init(gpio_if_t *mii_gpio) { uint32_t Data; int err; if (mii_gpio == NULL) { return -ENODEV; } /* * Initialize the GPIO component */ //TODO: CHANGE! err = gpio_init(mii_gpio, SYS_GPIO_0_BASE); if( err < 0 ) return err; /* * Set the direction for MDC signals to be output */ // GPIO_SetDataDirection(mii_gpio, MDC_MDIO_GPIO_CHANNEL, MDC_MDIO_MDIO_BIT); gpio_write(mii_gpio, GPIO_0_MASK_MDIO, GPIO_0_ALIGN_MDIO, GPIO_DIR_OFFSET, GPIO_0_MDIO_RST | GPIO_0_MDIO_MDC); return 0; }