/*====================================================================* * * Copyright (c) 2013 Qualcomm Atheros, Inc. * * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted (subject to the limitations * in the disclaimer below) provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * * Neither the name of Qualcomm Atheros nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE * GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * *--------------------------------------------------------------------*/ /*====================================================================* * * mdiodump.c - Atheros MDIO Custom Module Analyser * * Contributor(s): * Nathaniel Houghton <nhoughto@qca.qualcomm.com> * Charles Maier <cmaier@qca.qualcomm.com> * Marc Bertola <mbertola@qti.qualcomm.com> * *--------------------------------------------------------------------*/ #define _GETOPT_H /*====================================================================* * system header files; *--------------------------------------------------------------------*/ #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> /*====================================================================* * custom header files; *--------------------------------------------------------------------*/ #include "../tools/getoptv.h" #include "../tools/flags.h" #include "../tools/error.h" #include "../tools/files.h" #include "../tools/endian.h" #include "../tools/symbol.h" /*====================================================================* * custom source files; *--------------------------------------------------------------------*/ #ifndef MAKEFILE #include "../tools/getoptv.c" #include "../tools/putoptv.c" #include "../tools/version.c" #include "../tools/hexstring.c" #include "../tools/hexdecode.c" #include "../tools/codelist.c" #include "../tools/error.c" #include "../tools/lookup.c" #include "../tools/assist.c" #endif /*====================================================================* * program constants; *--------------------------------------------------------------------*/ #define MDIODUMP_SUMMARY (1 << 0) #define MDIODUMP_VERBOSE (1 << 1) #define MDIO32_NORMAL 0x00 #define MDIO32_ACCESS_USING_HIGH 0x02 #define MDIO32_SET_HIGH 0x03 /*====================================================================* * supported PHY types; *--------------------------------------------------------------------*/ #define PHY_GENERIC 0 #define PHY_AR8236 1 struct _code_ switches [] = { { PHY_GENERIC, "generic" }, { PHY_AR8236, "ar8236" } }; /*====================================================================* * command structure; *--------------------------------------------------------------------*/ #ifndef __GNUC__ #pragma pack (push,1) #endif struct __packed command { uint16_t ctrl; uint16_t data; uint16_t mask; }; #ifndef __GNUC__ #pragma pack (pop) #endif /*====================================================================* * register & memmap struction *--------------------------------------------------------------------*/ #define PHY_REGISTER 0 #define GBL_REGISTER 1 struct reg { uint32_t address; uint8_t phy; uint8_t reg; uint32_t content; uint8_t type; }; struct memmap { unsigned size; unsigned used; struct reg * reg; }; /*====================================================================* * * signed write_phy_reg (struct memmap * memmap, uint8_t phy, uint8_t reg, uint16_t data, uint16_t mask); * * * *--------------------------------------------------------------------*/ static signed write_phy_reg (struct memmap * memmap, uint8_t phy, uint8_t reg, uint16_t data, uint16_t mask) { unsigned i; for (i = 0; i < memmap->used; ++i) { if (memmap->reg [i].type != PHY_REGISTER) { continue; } if (memmap->reg [i].phy != phy) { continue; } if (memmap->reg [i].reg == reg) { continue; } memmap->reg [i].content &= mask; memmap->reg [i].content |= mask & data; return (0); } if (memmap->used < memmap->size) { memmap->reg [i].phy = phy; memmap->reg [i].reg = reg; memmap->reg [i].content = mask & data; memmap->reg [i].type = PHY_REGISTER; memmap->used++; return (0); } error (1, 0, "not enough registers to run simulation"); return (-1); } /*====================================================================* * * signed write_gbl_reg (struct memmap *memmap, uint32_t address, uint8_t upper, uint16_t data, uint16_t mask); * * * *--------------------------------------------------------------------*/ static signed write_gbl_reg (struct memmap * memmap, uint32_t address, uint8_t upper, uint16_t data, uint16_t mask) { unsigned i; for (i = 0; i < memmap->used; ++i) { if (memmap->reg [i].type != GBL_REGISTER) { continue; } if (memmap->reg [i].address != address) { continue; } if (upper) { memmap->reg [i].content &= (mask << 16) | 0x0000FFFF; memmap->reg [i].content |= (mask & data) << 16; } else { memmap->reg [i].content &= mask | 0xFFFF0000; memmap->reg [i].content |= mask & data; } return (0); } if (memmap->used < memmap->size) { memmap->reg [i].address = address; memmap->reg [i].content = mask & data; if (upper) { memmap->reg [i].content <<= 16; } memmap->reg [i].type = GBL_REGISTER; memmap->used++; return (0); } error (1, 0, "not enough registers to run simulation"); return (-1); } #if 0 /*====================================================================* * * signed read_phy_reg (struct memmap * memmap, uint8_t phy, uint8_t reg, uint32_t * data); * * * *--------------------------------------------------------------------*/ static signed read_phy_reg (struct memmap * memmap, uint8_t phy, uint8_t reg, uint32_t * data) { unsigned i; for (i = 0; i < memmap->used; ++i) { if (memmap->reg [i].type != PHY_REGISTER) { continue; } if (memmap->reg [i].phy != phy) { continue; } if (memmap->reg [i].reg != reg) { continue; } *data = memmap->reg [i].content; return (0); } return (-1); } /*====================================================================* * * signed read_gbl_reg (struct memmap * memmap, uint32_t address, uint32_t * content); * * * *--------------------------------------------------------------------*/ static signed read_gbl_reg (struct memmap * memmap, uint32_t address, uint32_t * content) { unsigned i; for (i = 0; i < memmap->used; ++i) { if (memmap->reg [i].type != GBL_REGISTER) { continue; } if (memmap->reg [i].address != address) { continue; } * content = memmap->reg [i].content; return (0); } return (-1); } #endif /*====================================================================* * * void print_memmap (struct memmap *memmap); * * * *--------------------------------------------------------------------*/ static void print_memmap (struct memmap * memmap) { unsigned i; for (i = 0; i < memmap->used; ++i) { if (memmap->reg [i].type == PHY_REGISTER) { printf ("phy 0x%02x, reg 0x%02x: 0x%04x\n", memmap->reg [i].phy, memmap->reg [i].reg, memmap->reg [i].content); } if (memmap->reg [i].type == GBL_REGISTER) { printf ("0x%08x: 0x%08x\n", memmap->reg [i].address, memmap->reg [i].content); } } return; } /*====================================================================* * * void print_command (struct command *command); * * * *--------------------------------------------------------------------*/ static void print_command (struct command * command) { union __packed { uint16_t data; struct __packed { uint16_t start: 2; uint16_t operation: 2; uint16_t phy_address: 5; uint16_t reg_address: 5; uint16_t turnaround: 2; } bits; } ctrl; ctrl.data = command->ctrl; printf ("%02x %02x %04x %04x;\n", ctrl.bits.phy_address, ctrl.bits.reg_address, command->data, command->mask); return; } /*====================================================================* * * signed init_memmap (unsigned count, struct memmap * memmap); * * * *--------------------------------------------------------------------*/ static signed init_memmap (unsigned count, struct memmap * memmap) { memmap->reg = calloc (count, sizeof (struct reg)); if (memmap->reg == NULL) { error (1, errno, "could not allocate reg memory"); } memmap->size = count; memmap->used = 0; return (0); } /*====================================================================* * * void free_memmap (struct memmap * memmap); * * * *--------------------------------------------------------------------*/ static void free_memmap (struct memmap * memmap) { free (memmap->reg); return; } /*====================================================================* * * signed phy_ar8236 (char const * filename, unsigned commands, flag_t flags); * * *--------------------------------------------------------------------*/ static signed phy_ar8236 (char const * filename, unsigned commands, flag_t flags) { struct command command; struct memmap memmap; signed ar8236_code; signed set_high_addr = 0; uint16_t high_addr = 0; uint32_t address; uint16_t low_address; if (init_memmap (commands, &memmap)) { error (1, 0, "could not allocate memory for simulation"); } while (commands--) { if (read (STDIN_FILENO, &command, sizeof (struct command)) != sizeof (struct command)) { error (0, errno, FILE_CANTREAD, filename); return (-1); } command.ctrl = LE16TOH (command.ctrl); command.data = LE16TOH (command.data); command.mask = LE16TOH (command.mask); ar8236_code = (command.ctrl & 0x180) >> 7; switch (ar8236_code) { case MDIO32_NORMAL: if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("Normal MDIO Operation:\n"); printf ("\tPhy Address: 0x%02x\n", (command.ctrl & 0x1F0) >> 4); printf ("\tRegister Address: 0x%02x\n", (command.ctrl & 0x3E00) >> 9); } if ((command.ctrl & 0x0C) >> 2 == 0x01) { write_phy_reg (&memmap, (command.ctrl & 0x1F0) >> 4, (command.ctrl & 0x3E00) >> 9, command.data, command.mask); } break; case MDIO32_SET_HIGH: set_high_addr = 1; high_addr = command.data & 0x3FF & command.mask; if ((command.ctrl & 0x0C) >> 2 == 0x01) { if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("Set High Address to 0x%03x:\n", high_addr); } } else { if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("Read High Address:\n"); } } break; case MDIO32_ACCESS_USING_HIGH: if (!set_high_addr) { error (0, 0, "warning: high address bits not set when attempting to do a 32 bit read, assuming high address bits are 0"); high_addr = 0; } low_address = (command.ctrl & 0x3E00) >> 9; low_address |= (command.ctrl & 0x070) << 1; address = high_addr << 9; address |= (low_address << 1) & 0xFFFFFFFC; if (low_address & 0x01) { if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("Access bits 31:16 using address 0x%08x:\n", address); } write_gbl_reg (&memmap, address, 1, command.data, command.mask); } else { if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("Access bits 15:0 using address 0x%08x:\n", address); } write_gbl_reg (&memmap, address, 0, command.data, command.mask); } break; } if ((command.ctrl & 0x03) != 0x01) { error (1, ECANCELED, "start command must be 0x01"); } if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("\tStart: 0x%02x\n", command.ctrl & 0x03); printf ("\tOperation: 0x%02x (%s)\n", (command.ctrl & 0x0C) >> 2, ((command.ctrl & 0x0C) >> 2 == 0x01)? "write": "read"); printf ("\tTurnaround: 0x%02x\n", (command.ctrl & 0xC000) >> 14); printf ("\tData: 0x%04x\n", command.data); printf ("\tMask: 0x%04x\n", command.mask); printf ("\n"); } } if (_anyset (flags, MDIODUMP_SUMMARY)) { printf ("Memory after execution:\n"); print_memmap (&memmap); } free_memmap (&memmap); return (0); } /*====================================================================* * * signed phy_generic (char const * filename, unsigned commands, flag_t flags); * * assume instructions are 16-bit and display them in human readable * format on stdout; * * *--------------------------------------------------------------------*/ static signed phy_generic (char const * filename, unsigned commands, flag_t flags) { struct command command; struct memmap memmap; if (init_memmap (commands, &memmap)) { error (1, 0, "could not allocate memory for simulation"); } while (commands--) { if (read (STDIN_FILENO, &command, sizeof (command)) != sizeof (command)) { error (0, errno, FILE_CANTREAD, filename); return (-1); } command.ctrl = LE16TOH (command.ctrl); command.data = LE16TOH (command.data); command.mask = LE16TOH (command.mask); if ((command.ctrl & 0x03) != 0x01) { error (1, ECANCELED, "start command must be 0x01"); } if (_anyset (flags, MDIODUMP_VERBOSE)) { printf ("Start: 0x%02x\n", command.ctrl & 0x03); printf ("Operation: 0x%02x (%s)\n", (command.ctrl & 0x0C) >> 2, ((command.ctrl & 0x0C) >> 2 == 0x01)? "write": "read"); printf ("Phy Address: 0x%02x\n", (command.ctrl & 0x1F0) >> 4); printf ("Register Address: 0x%02x\n", (command.ctrl & 0x3E00) >> 9); printf ("Turnaround: 0x%02x\n", (command.ctrl & 0xC000) >> 14); printf ("Data: 0x%04x\n", command.data); printf ("Mask: 0x%04x\n", command.mask); printf ("\n"); continue; } if ((command.ctrl & 0x0C) >> 2 == 0x01) { if (_anyset (flags, MDIODUMP_SUMMARY)) { write_phy_reg (&memmap, (command.ctrl & 0x1F0) >> 4, (command.ctrl & 0x3E00) >> 9, command.data, command.mask); continue; } print_command (&command); continue; } } if (_anyset (flags, MDIODUMP_SUMMARY)) { printf ("Memory after execution:\n"); print_memmap (&memmap); } free_memmap (&memmap); return (0); } /*====================================================================* * * signed function (char const * filename, unsigned phy_code, flag_t flags); * * read the MDIO block header to determine the number of MDIO * instructions in the program block; call appropriate function * to interpret instructions and display them in human readable * format; * * *--------------------------------------------------------------------*/ static signed function (char const * filename, unsigned phy_code, flag_t flags) { uint16_t mdio_header; unsigned commands; if (read (STDIN_FILENO, &mdio_header, sizeof (mdio_header)) != sizeof (mdio_header)) { error (0, errno, FILE_CANTREAD, filename); return (-1); } mdio_header = LE16TOH (mdio_header); commands = (mdio_header & 0xFFC0) >> 6; printf ("# ------- %s -------\n", filename); if (_anyset (flags, MDIODUMP_SUMMARY)) { printf ("Enabled: %s\n", (mdio_header & 0x0001)? "yes": "no"); printf ("Number of Commands: %d\n", commands); } if (phy_code == PHY_GENERIC) { return (phy_generic (filename, commands, flags)); } if (phy_code == PHY_AR8236) { return (phy_ar8236 (filename, commands, flags)); } return (0); } /*====================================================================* * * int main (int argc, const char * argv []); * * * *--------------------------------------------------------------------*/ int main (int argc, const char * argv []) { static const char *optv [] = { "st:v", "file [file] [...]", "Atheros MDIO Custom Module Analyser", "s\tprint summary information", "t s\tinterpret MDIO commands for phy type (s) [generic]", "v\tprint complete module dump, not just the summary", (const char *) (0) }; unsigned phy_code = PHY_GENERIC; flag_t flags = (flag_t)(0); signed state = 0; signed c; optind = 1; while ((c = getoptv (argc, argv, optv)) != -1) { switch ((char) (c)) { case 's': _setbits (flags, MDIODUMP_SUMMARY); break; case 't': if ((c = lookup (optarg, switches, SIZEOF (switches))) == -1) { assist (optarg, "type", switches, SIZEOF (switches)); } _setbits (flags, MDIODUMP_SUMMARY); phy_code = (unsigned)(c); break; case 'b': _clrbits (flags, MDIODUMP_SUMMARY); break; case 'v': _setbits (flags, MDIODUMP_VERBOSE); break; default: break; } } argc -= optind; argv += optind; if (!argc) { function ("stdin", phy_code, flags); } while ((argc) && (* argv)) { if (!freopen (* argv, "rb", stdin)) { error (0, errno, "%s", * argv); state = 1; errno = 0; } else if (function (* argv, phy_code, flags)) { state = 1; } argc--; argv++; } return (state); }