diff options
Diffstat (limited to 'board/gdsys/common')
-rw-r--r-- | board/gdsys/common/Makefile | 4 | ||||
-rw-r--r-- | board/gdsys/common/cmd_ioloop.c | 295 | ||||
-rw-r--r-- | board/gdsys/common/ihs_mdio.c | 88 | ||||
-rw-r--r-- | board/gdsys/common/ihs_mdio.h | 18 | ||||
-rw-r--r-- | board/gdsys/common/phy.c | 280 | ||||
-rw-r--r-- | board/gdsys/common/phy.h | 14 |
6 files changed, 698 insertions, 1 deletions
diff --git a/board/gdsys/common/Makefile b/board/gdsys/common/Makefile index 7f8b427..4957943 100644 --- a/board/gdsys/common/Makefile +++ b/board/gdsys/common/Makefile @@ -6,8 +6,10 @@ # obj-$(CONFIG_SYS_FPGA_COMMON) += fpga.o +obj-$(CONFIG_CMD_IOLOOP) += cmd_ioloop.o obj-$(CONFIG_IO) += miiphybb.o obj-$(CONFIG_IO64) += miiphybb.o -obj-$(CONFIG_IOCON) += osd.o mclink.o dp501.o +obj-$(CONFIG_IOCON) += osd.o mclink.o dp501.o phy.o obj-$(CONFIG_DLVISION_10G) += osd.o obj-$(CONFIG_CONTROLCENTERD) += dp501.o +obj-$(CONFIG_HRCON) += osd.o mclink.o dp501.o phy.o diff --git a/board/gdsys/common/cmd_ioloop.c b/board/gdsys/common/cmd_ioloop.c new file mode 100644 index 0000000..e0c74fe --- /dev/null +++ b/board/gdsys/common/cmd_ioloop.c @@ -0,0 +1,295 @@ +/* + * (C) Copyright 2014 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <command.h> + +#include <gdsys_fpga.h> + +enum { + STATE_TX_PACKET_BUILDING = 1<<0, + STATE_TX_TRANSMITTING = 1<<1, + STATE_TX_BUFFER_FULL = 1<<2, + STATE_TX_ERR = 1<<3, + STATE_RECEIVE_TIMEOUT = 1<<4, + STATE_PROC_RX_STORE_TIMEOUT = 1<<5, + STATE_PROC_RX_RECEIVE_TIMEOUT = 1<<6, + STATE_RX_DIST_ERR = 1<<7, + STATE_RX_LENGTH_ERR = 1<<8, + STATE_RX_FRAME_CTR_ERR = 1<<9, + STATE_RX_FCS_ERR = 1<<10, + STATE_RX_PACKET_DROPPED = 1<<11, + STATE_RX_DATA_LAST = 1<<12, + STATE_RX_DATA_FIRST = 1<<13, + STATE_RX_DATA_AVAILABLE = 1<<15, +}; + +enum { + CTRL_PROC_RECEIVE_ENABLE = 1<<12, + CTRL_FLUSH_TRANSMIT_BUFFER = 1<<15, +}; + +enum { + IRQ_CPU_TRANSMITBUFFER_FREE_STATUS = 1<<5, + IRQ_CPU_PACKET_TRANSMITTED_EVENT = 1<<6, + IRQ_NEW_CPU_PACKET_RECEIVED_EVENT = 1<<7, + IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS = 1<<8, +}; + +struct io_generic_packet { + u16 target_address; + u16 source_address; + u8 packet_type; + u8 bc; + u16 packet_length; +} __attribute__((__packed__)); + +unsigned long long rx_ctr; +unsigned long long tx_ctr; +unsigned long long err_ctr; + +static void io_check_status(unsigned int fpga, u16 status, bool silent) +{ + u16 mask = STATE_RX_DIST_ERR | STATE_RX_LENGTH_ERR | + STATE_RX_FRAME_CTR_ERR | STATE_RX_FCS_ERR | + STATE_RX_PACKET_DROPPED | STATE_TX_ERR; + + if (!(status & mask)) { + FPGA_SET_REG(fpga, ep.rx_tx_status, status); + return; + } + + err_ctr++; + FPGA_SET_REG(fpga, ep.rx_tx_status, status); + + if (silent) + return; + + if (status & STATE_RX_PACKET_DROPPED) + printf("RX_PACKET_DROPPED, status %04x\n", status); + + if (status & STATE_RX_DIST_ERR) + printf("RX_DIST_ERR\n"); + if (status & STATE_RX_LENGTH_ERR) + printf("RX_LENGTH_ERR\n"); + if (status & STATE_RX_FRAME_CTR_ERR) + printf("RX_FRAME_CTR_ERR\n"); + if (status & STATE_RX_FCS_ERR) + printf("RX_FCS_ERR\n"); + + if (status & STATE_TX_ERR) + printf("TX_ERR\n"); +} + +static void io_send(unsigned int fpga, unsigned int size) +{ + unsigned int k; + struct io_generic_packet packet = { + .source_address = 1, + .packet_type = 1, + .packet_length = size, + }; + u16 *p = (u16 *)&packet; + + for (k = 0; k < sizeof(packet) / 2; ++k) + FPGA_SET_REG(fpga, ep.transmit_data, *p++); + + for (k = 0; k < (size + 1) / 2; ++k) + FPGA_SET_REG(fpga, ep.transmit_data, k); + + FPGA_SET_REG(fpga, ep.rx_tx_control, + CTRL_PROC_RECEIVE_ENABLE | CTRL_FLUSH_TRANSMIT_BUFFER); + + tx_ctr++; +} + +static void io_receive(unsigned int fpga) +{ + unsigned int k = 0; + u16 rx_tx_status; + + FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status); + + while (rx_tx_status & STATE_RX_DATA_AVAILABLE) { + u16 rx; + + if (rx_tx_status & STATE_RX_DATA_LAST) + rx_ctr++; + + FPGA_GET_REG(fpga, ep.receive_data, &rx); + + FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status); + + ++k; + } +} + +static void io_reflect(unsigned int fpga) +{ + u16 buffer[128]; + + unsigned int k = 0; + unsigned int n; + u16 rx_tx_status; + + FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status); + + while (rx_tx_status & STATE_RX_DATA_AVAILABLE) { + FPGA_GET_REG(fpga, ep.receive_data, &buffer[k++]); + if (rx_tx_status & STATE_RX_DATA_LAST) + break; + + FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status); + } + + if (!k) + return; + + for (n = 0; n < k; ++n) + FPGA_SET_REG(fpga, ep.transmit_data, buffer[n]); + + FPGA_SET_REG(fpga, ep.rx_tx_control, + CTRL_PROC_RECEIVE_ENABLE | CTRL_FLUSH_TRANSMIT_BUFFER); + + tx_ctr++; +} + +/* + * FPGA io-endpoint reflector + * + * Syntax: + * ioreflect {fpga} {reportrate} + */ +int do_ioreflect(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int fpga; + unsigned int rate = 0; + unsigned long long last_seen = 0; + + if (argc < 2) + return CMD_RET_USAGE; + + fpga = simple_strtoul(argv[1], NULL, 10); + + /* + * If another parameter, it is the report rate in packets. + */ + if (argc > 2) + rate = simple_strtoul(argv[2], NULL, 10); + + /* enable receive path */ + FPGA_SET_REG(fpga, ep.rx_tx_control, CTRL_PROC_RECEIVE_ENABLE); + + /* set device address to dummy 1*/ + FPGA_SET_REG(fpga, ep.device_address, 1); + + rx_ctr = 0; tx_ctr = 0; err_ctr = 0; + + while (1) { + u16 top_int; + u16 rx_tx_status; + + FPGA_GET_REG(fpga, top_interrupt, &top_int); + FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status); + + io_check_status(fpga, rx_tx_status, true); + if ((top_int & IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS) && + (top_int & IRQ_CPU_TRANSMITBUFFER_FREE_STATUS)) + io_reflect(fpga); + + if (rate) { + if (!(tx_ctr % rate) && (tx_ctr != last_seen)) + printf("refl %llu, err %llu\n", tx_ctr, + err_ctr); + last_seen = tx_ctr; + } + + if (ctrlc()) + break; + } + + return 0; +} + +/* + * FPGA io-endpoint looptest + * + * Syntax: + * ioloop {fpga} {size} {rate} + */ +#define DISP_LINE_LEN 16 +int do_ioloop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + unsigned int fpga; + unsigned int size; + unsigned int rate = 0; + + if (argc < 3) + return CMD_RET_USAGE; + + /* + * FPGA is specified since argc > 2 + */ + fpga = simple_strtoul(argv[1], NULL, 10); + + /* + * packet size is specified since argc > 2 + */ + size = simple_strtoul(argv[2], NULL, 10); + + /* + * If another parameter, it is the test rate in packets per second. + */ + if (argc > 3) + rate = simple_strtoul(argv[3], NULL, 10); + + /* enable receive path */ + FPGA_SET_REG(fpga, ep.rx_tx_control, CTRL_PROC_RECEIVE_ENABLE); + + /* set device address to dummy 1*/ + FPGA_SET_REG(fpga, ep.device_address, 1); + + rx_ctr = 0; tx_ctr = 0; err_ctr = 0; + + while (1) { + u16 top_int; + u16 rx_tx_status; + + FPGA_GET_REG(fpga, top_interrupt, &top_int); + FPGA_GET_REG(fpga, ep.rx_tx_status, &rx_tx_status); + + io_check_status(fpga, rx_tx_status, false); + if (top_int & IRQ_CPU_TRANSMITBUFFER_FREE_STATUS) + io_send(fpga, size); + if (top_int & IRQ_CPU_RECEIVE_DATA_AVAILABLE_STATUS) + io_receive(fpga); + + if (rate) { + if (ctrlc()) + break; + udelay(1000000 / rate); + if (!(tx_ctr % rate)) + printf("d %lld, tx %llu, rx %llu, err %llu\n", + tx_ctr - rx_ctr, tx_ctr, rx_ctr, + err_ctr); + } + } + + return 0; +} + +U_BOOT_CMD( + ioloop, 4, 0, do_ioloop, + "fpga io-endpoint looptest", + "fpga packetsize [packets/sec]" +); + +U_BOOT_CMD( + ioreflect, 3, 0, do_ioreflect, + "fpga io-endpoint reflector", + "fpga reportrate" +); diff --git a/board/gdsys/common/ihs_mdio.c b/board/gdsys/common/ihs_mdio.c new file mode 100644 index 0000000..1d6eb7b --- /dev/null +++ b/board/gdsys/common/ihs_mdio.c @@ -0,0 +1,88 @@ +/* + * (C) Copyright 2014 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <gdsys_fpga.h> +#include <miiphy.h> + +#include "ihs_mdio.h" + +static int ihs_mdio_idle(struct mii_dev *bus) +{ + struct ihs_mdio_info *info = bus->priv; + u16 val; + unsigned int ctr = 0; + + do { + FPGA_GET_REG(info->fpga, mdio.control, &val); + udelay(100); + if (ctr++ > 10) + return -1; + } while (!(val & (1 << 12))); + + return 0; +} + +static int ihs_mdio_reset(struct mii_dev *bus) +{ + ihs_mdio_idle(bus); + + return 0; +} + +static int ihs_mdio_read(struct mii_dev *bus, int addr, int dev_addr, + int regnum) +{ + struct ihs_mdio_info *info = bus->priv; + u16 val; + + ihs_mdio_idle(bus); + + FPGA_SET_REG(info->fpga, mdio.control, + ((addr & 0x1f) << 5) | (regnum & 0x1f) | (2 << 10)); + + /* wait for rx data available */ + udelay(100); + + FPGA_GET_REG(info->fpga, mdio.rx_data, &val); + + return val; +} + +static int ihs_mdio_write(struct mii_dev *bus, int addr, int dev_addr, + int regnum, u16 value) +{ + struct ihs_mdio_info *info = bus->priv; + + ihs_mdio_idle(bus); + + FPGA_SET_REG(info->fpga, mdio.address_data, value); + FPGA_SET_REG(info->fpga, mdio.control, + ((addr & 0x1f) << 5) | (regnum & 0x1f) | (1 << 10)); + + return 0; +} + +int ihs_mdio_init(struct ihs_mdio_info *info) +{ + struct mii_dev *bus = mdio_alloc(); + + if (!bus) { + printf("Failed to allocate FSL MDIO bus\n"); + return -1; + } + + bus->read = ihs_mdio_read; + bus->write = ihs_mdio_write; + bus->reset = ihs_mdio_reset; + sprintf(bus->name, info->name); + + bus->priv = info; + + return mdio_register(bus); +} diff --git a/board/gdsys/common/ihs_mdio.h b/board/gdsys/common/ihs_mdio.h new file mode 100644 index 0000000..64b4049 --- /dev/null +++ b/board/gdsys/common/ihs_mdio.h @@ -0,0 +1,18 @@ +/* + * (C) Copyright 2014 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _IHS_MDIO_H_ +#define _IHS_MDIO_H_ + +struct ihs_mdio_info { + u32 fpga; + char *name; +}; + +int ihs_mdio_init(struct ihs_mdio_info *info); + +#endif diff --git a/board/gdsys/common/phy.c b/board/gdsys/common/phy.c new file mode 100644 index 0000000..fb92658 --- /dev/null +++ b/board/gdsys/common/phy.c @@ -0,0 +1,280 @@ +/* + * (C) Copyright 2014 + * Dirk Eibach, Guntermann & Drunck GmbH, eibach@gdsys.de + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> + +#include <miiphy.h> + +enum { + MIICMD_SET, + MIICMD_MODIFY, + MIICMD_VERIFY_VALUE, + MIICMD_WAIT_FOR_VALUE, +}; + +struct mii_setupcmd { + u8 token; + u8 reg; + u16 data; + u16 mask; + u32 timeout; +}; + +/* + * verify we are talking to a 88e1518 + */ +struct mii_setupcmd verify_88e1518[] = { + { MIICMD_SET, 22, 0x0000 }, + { MIICMD_VERIFY_VALUE, 2, 0x0141, 0xffff }, + { MIICMD_VERIFY_VALUE, 3, 0x0dd0, 0xfff0 }, +}; + +/* + * workaround for erratum mentioned in 88E1518 release notes + */ +struct mii_setupcmd fixup_88e1518[] = { + { MIICMD_SET, 22, 0x00ff }, + { MIICMD_SET, 17, 0x214b }, + { MIICMD_SET, 16, 0x2144 }, + { MIICMD_SET, 17, 0x0c28 }, + { MIICMD_SET, 16, 0x2146 }, + { MIICMD_SET, 17, 0xb233 }, + { MIICMD_SET, 16, 0x214d }, + { MIICMD_SET, 17, 0xcc0c }, + { MIICMD_SET, 16, 0x2159 }, + { MIICMD_SET, 22, 0x00fb }, + { MIICMD_SET, 7, 0xc00d }, + { MIICMD_SET, 22, 0x0000 }, +}; + +/* + * default initialization: + * - set RGMII receive timing to "receive clock transition when data stable" + * - set RGMII transmit timing to "transmit clock internally delayed" + * - set RGMII output impedance target to 78,8 Ohm + * - run output impedance calibration + * - set autonegotiation advertise to 1000FD only + */ +struct mii_setupcmd default_88e1518[] = { + { MIICMD_SET, 22, 0x0002 }, + { MIICMD_MODIFY, 21, 0x0030, 0x0030 }, + { MIICMD_MODIFY, 25, 0x0000, 0x0003 }, + { MIICMD_MODIFY, 24, 0x8000, 0x8000 }, + { MIICMD_WAIT_FOR_VALUE, 24, 0x4000, 0x4000, 2000 }, + { MIICMD_SET, 22, 0x0000 }, + { MIICMD_MODIFY, 4, 0x0000, 0x01e0 }, + { MIICMD_MODIFY, 9, 0x0200, 0x0300 }, +}; + +/* + * turn off CLK125 for PHY daughterboard + */ +struct mii_setupcmd ch1fix_88e1518[] = { + { MIICMD_SET, 22, 0x0002 }, + { MIICMD_MODIFY, 16, 0x0006, 0x0006 }, + { MIICMD_SET, 22, 0x0000 }, +}; + +/* + * perform copper software reset + */ +struct mii_setupcmd swreset_88e1518[] = { + { MIICMD_SET, 22, 0x0000 }, + { MIICMD_MODIFY, 0, 0x8000, 0x8000 }, + { MIICMD_WAIT_FOR_VALUE, 0, 0x0000, 0x8000, 2000 }, +}; + +/* + * special one for 88E1514: + * Force SGMII to Copper mode + */ +struct mii_setupcmd mii_to_copper_88e1514[] = { + { MIICMD_SET, 22, 0x0012 }, + { MIICMD_MODIFY, 20, 0x0001, 0x0007 }, + { MIICMD_MODIFY, 20, 0x8000, 0x8000 }, + { MIICMD_SET, 22, 0x0000 }, +}; + +/* + * turn off SGMII auto-negotiation + */ +struct mii_setupcmd sgmii_autoneg_off_88e1518[] = { + { MIICMD_SET, 22, 0x0001 }, + { MIICMD_MODIFY, 0, 0x0000, 0x1000 }, + { MIICMD_MODIFY, 0, 0x8000, 0x8000 }, + { MIICMD_SET, 22, 0x0000 }, +}; + +/* + * invert LED2 polarity + */ +struct mii_setupcmd invert_led2_88e1514[] = { + { MIICMD_SET, 22, 0x0003 }, + { MIICMD_MODIFY, 17, 0x0030, 0x0010 }, + { MIICMD_SET, 22, 0x0000 }, +}; + +static int process_setupcmd(const char *bus, unsigned char addr, + struct mii_setupcmd *setupcmd) +{ + int res; + u8 reg = setupcmd->reg; + u16 data = setupcmd->data; + u16 mask = setupcmd->mask; + u32 timeout = setupcmd->timeout; + u16 orig_data; + unsigned long start; + + debug("mii %s:%u reg %2u ", bus, addr, reg); + + switch (setupcmd->token) { + case MIICMD_MODIFY: + res = miiphy_read(bus, addr, reg, &orig_data); + if (res) + break; + debug("is %04x. (value %04x mask %04x) ", orig_data, data, + mask); + data = (orig_data & ~mask) | (data & mask); + /* fallthrough */ + case MIICMD_SET: + debug("=> %04x\n", data); + res = miiphy_write(bus, addr, reg, data); + break; + case MIICMD_VERIFY_VALUE: + res = miiphy_read(bus, addr, reg, &orig_data); + if (res) + break; + if ((orig_data & mask) != (data & mask)) + res = -1; + debug("(value %04x mask %04x) == %04x? %s\n", data, mask, + orig_data, res ? "FAIL" : "PASS"); + break; + case MIICMD_WAIT_FOR_VALUE: + res = -1; + start = get_timer(0); + while ((res != 0) && (get_timer(start) < timeout)) { + res = miiphy_read(bus, addr, reg, &orig_data); + if (res) + continue; + if ((orig_data & mask) != (data & mask)) + res = -1; + } + debug("(value %04x mask %04x) == %04x? %s after %lu ms\n", data, + mask, orig_data, res ? "FAIL" : "PASS", + get_timer(start)); + break; + default: + res = -1; + break; + } + + return res; +} + +static int process_setup(const char *bus, unsigned char addr, + struct mii_setupcmd *setupcmd, unsigned int count) +{ + int res = 0; + unsigned int k; + + for (k = 0; k < count; ++k) { + res = process_setupcmd(bus, addr, &setupcmd[k]); + if (res) { + printf("mii cmd %u on bus %s addr %u failed, aborting setup\n", + setupcmd[k].token, bus, addr); + break; + } + } + + return res; +} + +int setup_88e1518(const char *bus, unsigned char addr) +{ + int res; + + res = process_setup(bus, addr, + verify_88e1518, ARRAY_SIZE(verify_88e1518)); + if (res) + return res; + + res = process_setup(bus, addr, + fixup_88e1518, ARRAY_SIZE(fixup_88e1518)); + if (res) + return res; + + res = process_setup(bus, addr, + default_88e1518, ARRAY_SIZE(default_88e1518)); + if (res) + return res; + + if (addr) { + res = process_setup(bus, addr, + ch1fix_88e1518, ARRAY_SIZE(ch1fix_88e1518)); + if (res) + return res; + } + + res = process_setup(bus, addr, + swreset_88e1518, ARRAY_SIZE(swreset_88e1518)); + if (res) + return res; + + return 0; +} + +int setup_88e1514(const char *bus, unsigned char addr) +{ + int res; + + res = process_setup(bus, addr, + verify_88e1518, ARRAY_SIZE(verify_88e1518)); + if (res) + return res; + + res = process_setup(bus, addr, + fixup_88e1518, ARRAY_SIZE(fixup_88e1518)); + if (res) + return res; + + res = process_setup(bus, addr, + mii_to_copper_88e1514, + ARRAY_SIZE(mii_to_copper_88e1514)); + if (res) + return res; + + res = process_setup(bus, addr, + sgmii_autoneg_off_88e1518, + ARRAY_SIZE(sgmii_autoneg_off_88e1518)); + if (res) + return res; + + res = process_setup(bus, addr, + invert_led2_88e1514, + ARRAY_SIZE(invert_led2_88e1514)); + if (res) + return res; + + res = process_setup(bus, addr, + default_88e1518, ARRAY_SIZE(default_88e1518)); + if (res) + return res; + + if (addr) { + res = process_setup(bus, addr, + ch1fix_88e1518, ARRAY_SIZE(ch1fix_88e1518)); + if (res) + return res; + } + + res = process_setup(bus, addr, + swreset_88e1518, ARRAY_SIZE(swreset_88e1518)); + if (res) + return res; + + return 0; +} diff --git a/board/gdsys/common/phy.h b/board/gdsys/common/phy.h new file mode 100644 index 0000000..afbdc65 --- /dev/null +++ b/board/gdsys/common/phy.h @@ -0,0 +1,14 @@ +/* + * (C) Copyright 2014 + * Dirk Eibach, Guntermann & Drunck GmbH, dirk.eibach@gdsys.cc + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _PHY_H_ +#define _PHY_H_ + +int setup_88e1514(const char *bus, unsigned char addr); +int setup_88e1518(const char *bus, unsigned char addr); + +#endif |