root/usr/src/uts/common/io/nxge/npi/npi_espc.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

#include <npi_espc.h>
#include <nxge_espc.h>

static int npi_vpd_read_prop(npi_handle_t handle, uint32_t ep,
                const char *prop, int len, char *val);

npi_status_t
npi_espc_pio_enable(npi_handle_t handle)
{
        NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_EN_REG), 0x1);
        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_pio_disable(npi_handle_t handle)
{
        NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_EN_REG), 0);
        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_eeprom_entry(npi_handle_t handle, io_op_t op, uint32_t addr,
                        uint8_t *data)
{
        uint64_t val = 0;

        if ((addr & ~EPC_EEPROM_ADDR_BITS) != 0) {
                NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                    " npi_espc_eerprom_entry"
                    " Invalid input addr <0x%x>\n",
                    addr));
                return (NPI_FAILURE | NPI_ESPC_EEPROM_ADDR_INVALID);
        }

        switch (op) {
        case OP_SET:
                val = EPC_WRITE_INITIATE | (addr << EPC_EEPROM_ADDR_SHIFT) |
                    *data;
                NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), val);
                EPC_WAIT_RW_COMP(handle, &val, EPC_WRITE_COMPLETE);
                if ((val & EPC_WRITE_COMPLETE) == 0) {
                        NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                            " npi_espc_eeprom_entry"
                            " HW Error: EEPROM_WR <0x%x>\n",
                            val));
                        return (NPI_FAILURE | NPI_ESPC_EEPROM_WRITE_FAILED);
                }
                break;
        case OP_GET:
                val = EPC_READ_INITIATE | (addr << EPC_EEPROM_ADDR_SHIFT);
                NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), val);
                EPC_WAIT_RW_COMP(handle, &val, EPC_READ_COMPLETE);
                if ((val & EPC_READ_COMPLETE) == 0) {
                        NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                            " npi_espc_eeprom_entry"
                            " HW Error: EEPROM_RD <0x%x>",
                            val));
                        return (NPI_FAILURE | NPI_ESPC_EEPROM_READ_FAILED);
                }
                NXGE_REG_RD64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), &val);
                /*
                 * Workaround for synchronization issues - do a second PIO
                 */
                val = EPC_READ_INITIATE | (addr << EPC_EEPROM_ADDR_SHIFT);
                NXGE_REG_WR64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), val);
                EPC_WAIT_RW_COMP(handle, &val, EPC_READ_COMPLETE);
                if ((val & EPC_READ_COMPLETE) == 0) {
                        NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                            " npi_espc_eeprom_entry HW Error: "
                            "EEPROM_RD <0x%x>", val));
                        return (NPI_FAILURE | NPI_ESPC_EEPROM_READ_FAILED);
                }
                NXGE_REG_RD64(handle, ESPC_REG_ADDR(ESPC_PIO_STATUS_REG), &val);
                *data = val & EPC_EEPROM_DATA_MASK;
                break;
        default:
                NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                    " npi_espc_eeprom_entry"
                    " Invalid Input addr <0x%x>\n", addr));
                return (NPI_FAILURE | NPI_ESPC_OPCODE_INVALID);
        }

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_mac_addr_get(npi_handle_t handle, uint8_t *data)
{
        mac_addr_0_t mac0;
        mac_addr_1_t mac1;

        NXGE_REG_RD64(handle, ESPC_MAC_ADDR_0, &mac0.value);
        data[0] = mac0.bits.w0.byte0;
        data[1] = mac0.bits.w0.byte1;
        data[2] = mac0.bits.w0.byte2;
        data[3] = mac0.bits.w0.byte3;

        NXGE_REG_RD64(handle, ESPC_MAC_ADDR_1, &mac1.value);
        data[4] = mac1.bits.w0.byte4;
        data[5] = mac1.bits.w0.byte5;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_num_ports_get(npi_handle_t handle, uint8_t *data)
{
        uint64_t val = 0;

        NXGE_REG_RD64(handle, ESPC_NUM_PORTS_MACS, &val);
        val &= NUM_PORTS_MASK;
        *data = (uint8_t)val;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_num_macs_get(npi_handle_t handle, uint8_t *data)
{
        uint64_t val = 0;

        NXGE_REG_RD64(handle, ESPC_NUM_PORTS_MACS, &val);
        val &= NUM_MAC_ADDRS_MASK;
        val = (val >> NUM_MAC_ADDRS_SHIFT);
        *data = (uint8_t)val;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_model_str_get(npi_handle_t handle, char *data)
{
        uint64_t val = 0;
        uint16_t str_len;
        int i, j;

        NXGE_REG_RD64(handle, ESPC_MOD_STR_LEN, &val);
        val &= MOD_STR_LEN_MASK;
        str_len = (uint8_t)val;

        if (str_len > MAX_MOD_STR_LEN) {
                NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                    " npi_espc_model_str_get"
                    " Model string length %d exceeds max %d\n",
                    str_len, MAX_MOD_STR_LEN));
                return (NPI_FAILURE | NPI_ESPC_STR_LEN_INVALID);
        }

        /*
         * Might have to reverse the order depending on how the string
         * is written.
         */
        for (i = 0, j = 0; i < str_len; j++) {
                NXGE_REG_RD64(handle, ESPC_MOD_STR(j), &val);
                data[i++] = ((char *)&val)[3];
                data[i++] = ((char *)&val)[2];
                data[i++] = ((char *)&val)[1];
                data[i++] = ((char *)&val)[0];
        }

        data[str_len] = '\0';

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_bd_model_str_get(npi_handle_t handle, char *data)
{
        uint64_t val = 0;
        uint16_t str_len;
        int i, j;

        NXGE_REG_RD64(handle, ESPC_BD_MOD_STR_LEN, &val);
        val &= BD_MOD_STR_LEN_MASK;
        str_len = (uint8_t)val;

        if (str_len > MAX_BD_MOD_STR_LEN) {
                NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                    " npi_espc_model_str_get"
                    " Board Model string length %d "
                    "exceeds max %d\n",
                    str_len, MAX_BD_MOD_STR_LEN));
                return (NPI_FAILURE | NPI_ESPC_STR_LEN_INVALID);
        }

        /*
         * Might have to reverse the order depending on how the string
         * is written.
         */
        for (i = 0, j = 0; i < str_len; j++) {
                NXGE_REG_RD64(handle, ESPC_BD_MOD_STR(j), &val);
                data[i++] = ((char *)&val)[3];
                data[i++] = ((char *)&val)[2];
                data[i++] = ((char *)&val)[1];
                data[i++] = ((char *)&val)[0];
        }

        data[str_len] = '\0';

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_phy_type_get(npi_handle_t handle, uint8_t *data)
{
        phy_type_t      phy;

        NXGE_REG_RD64(handle, ESPC_PHY_TYPE, &phy.value);
        data[0] = phy.bits.w0.pt0_phy_type;
        data[1] = phy.bits.w0.pt1_phy_type;
        data[2] = phy.bits.w0.pt2_phy_type;
        data[3] = phy.bits.w0.pt3_phy_type;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_port_phy_type_get(npi_handle_t handle, uint8_t *data, uint8_t portn)
{
        phy_type_t      phy;

        ASSERT(IS_PORT_NUM_VALID(portn));

        NXGE_REG_RD64(handle, ESPC_PHY_TYPE, &phy.value);
        switch (portn) {
        case 0:
                *data = phy.bits.w0.pt0_phy_type;
                break;
        case 1:
                *data = phy.bits.w0.pt1_phy_type;
                break;
        case 2:
                *data = phy.bits.w0.pt2_phy_type;
                break;
        case 3:
                *data = phy.bits.w0.pt3_phy_type;
                break;
        default:
                NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                    " npi_espc_port_phy_type_get"
                    " Invalid Input: portn <%d>",
                    portn));
                return (NPI_FAILURE | NPI_ESPC_PORT_INVALID);
        }

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_max_frame_get(npi_handle_t handle, uint16_t *data)
{
        uint64_t val = 0;

        NXGE_REG_RD64(handle, ESPC_MAX_FM_SZ, &val);
        val &= MAX_FM_SZ_MASK;
        *data = (uint8_t)val;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_version_get(npi_handle_t handle, uint16_t *data)
{
        uint64_t val = 0;

        NXGE_REG_RD64(handle, ESPC_VER_IMGSZ, &val);
        val &= VER_NUM_MASK;
        *data = (uint8_t)val;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_img_sz_get(npi_handle_t handle, uint16_t *data)
{
        uint64_t val = 0;

        NXGE_REG_RD64(handle, ESPC_VER_IMGSZ, &val);
        val &= IMG_SZ_MASK;
        val = val >> IMG_SZ_SHIFT;
        *data = (uint8_t)val;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_chksum_get(npi_handle_t handle, uint8_t *data)
{
        uint64_t val = 0;

        NXGE_REG_RD64(handle, ESPC_CHKSUM, &val);
        val &= CHKSUM_MASK;
        *data = (uint8_t)val;

        return (NPI_SUCCESS);
}

npi_status_t
npi_espc_intr_num_get(npi_handle_t handle, uint8_t *data)
{
        intr_num_t      intr;

        NXGE_REG_RD64(handle, ESPC_INTR_NUM, &intr.value);
        data[0] = intr.bits.w0.pt0_intr_num;
        data[1] = intr.bits.w0.pt1_intr_num;
        data[2] = intr.bits.w0.pt2_intr_num;
        data[3] = intr.bits.w0.pt3_intr_num;

        return (NPI_SUCCESS);
}

void
npi_espc_dump(npi_handle_t handle)
{
        int i;
        uint64_t val = 0;

        NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL,
            "Dumping SEEPROM registers directly:\n\n"));

        for (i = 0; i < 23; i++) {
                NXGE_REG_RD64(handle, ESPC_NCR_REGN(i), &val);
                NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL,
                    "reg[%d]      0x%llx\n",
                    i, val & 0xffffffff));
        }

        NPI_REG_DUMP_MSG((handle.function, NPI_REG_CTL, "\n\n"));
}

uint32_t
npi_espc_reg_get(npi_handle_t handle, int reg_idx)
{
        uint64_t val = 0;
        uint32_t reg_val = 0;

        NXGE_REG_RD64(handle, ESPC_NCR_REGN(reg_idx), &val);
        reg_val = val & 0xffffffff;

        return (reg_val);
}

static inline uint8_t vpd_rd(npi_handle_t handle, uint32_t addr)
{
        uint8_t data = 0;

        if (npi_espc_eeprom_entry(handle, OP_GET, addr, &data) != NPI_SUCCESS)
                data = 0;
        return (data);
}

npi_status_t
npi_espc_vpd_info_get(npi_handle_t handle, p_npi_vpd_info_t vpdp,
                uint32_t rom_len)
{
        int             i, len;
        uint32_t        base = 0, kstart = 0, ep, end;
        uint8_t         fd_flags = 0;

        /* Fill the vpd_info struct with invalid vals */
        (void) strcpy(vpdp->model, "\0");
        (void) strcpy(vpdp->bd_model, "\0");
        (void) strcpy(vpdp->phy_type, "\0");
        (void) strcpy(vpdp->ver, "\0");
        vpdp->num_macs = 0;
        for (i = 0; i < ETHERADDRL; i++) {
                vpdp->mac_addr[i] = 0;
        }

        ep = 0;
        end = ep + rom_len;

        /* go through the images till OBP image type is found */
        while (ep < end) {
                base = ep;
                /* check for expansion rom header signature */
                if (vpd_rd(handle, ep) != 0x55 ||
                    vpd_rd(handle, ep + 1) != 0xaa) {
                        NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                            "npi_espc_vpd_info_get: expansion rom image "
                            "not found, 0x%x [0x%x 0x%x]", ep,
                            vpd_rd(handle, ep), vpd_rd(handle, ep + 1)));
                        goto vpd_info_err;
                }
                /* go to the beginning of the PCI data struct of this image */
                ep = ep + 23;
                ep = base + ((vpd_rd(handle, ep) << 8) |
                    (vpd_rd(handle, ep + 1)));
                /* check for PCI data struct signature "PCIR" */
                if ((vpd_rd(handle, ep) != 0x50) ||
                    (vpd_rd(handle, ep + 1) != 0x43) ||
                    (vpd_rd(handle, ep + 2) != 0x49) ||
                    (vpd_rd(handle, ep + 3) != 0x52)) {
                        NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                            "npi_espc_vpd_info_get: PCIR sig not found"));
                        goto vpd_info_err;
                }
                /* check for image type OBP */
                if (vpd_rd(handle, ep + 20) != 0x01) {
                        /* go to the next image */
                        ep = base + ((vpd_rd(handle, base + 2)) * 512);
                        continue;
                }
                /* find the beginning of the VPD data */
                base = base + (vpd_rd(handle, ep + 8) |
                    (vpd_rd(handle, ep + 9) << 8));
                break;
        }

        /* check first byte of identifier string tag */
        if (!base || (vpd_rd(handle, base + 0) != 0x82)) {
                NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                    "npi_espc_vpd_info_get: Could not find VPD!!"));
                goto vpd_info_err;
        }

        /*
         * skip over the ID string descriptor to go to the read-only VPD
         * keywords list.
         */
        i = (vpd_rd(handle, base + 1) |
            (vpd_rd(handle, base + 2) << 8)) + 3;

        while (i < EXPANSION_ROM_SIZE) {
                if (vpd_rd(handle, base + i) != 0x90) { /* no vpd found */
                        NPI_ERROR_MSG((handle.function, NPI_ERR_CTL,
                            "nxge_get_vpd_info: Could not find "
                            "VPD ReadOnly list!! [0x%x] %d",
                            vpd_rd(handle, base + i), i));
                        goto vpd_info_err;
                }

                /* found a vpd read-only list, get its length */
                len = vpd_rd(handle, base + i + 1) |
                    (vpd_rd(handle, base + i + 2) << 8);

                /* extract keywords */
                kstart = base + i + 3;
                ep = kstart;
                /*
                 * Each keyword field is as follows:
                 * 2 bytes keyword in the form of "Zx" where x = 0,1,2....
                 * 1 byte keyword data field length - klen
                 * Now the actual keyword data field:
                 *      1 byte VPD property instance, 'M' / 'I'
                 *      2 bytes
                 *      1 byte VPD property data type, 'B' / 'S'
                 *      1 byte VPD property value length - n
                 *      Actual property string, length (klen - n - 5) bytes
                 *      Actual property value, length n bytes
                 */
                while ((ep - kstart) < len) {
                        int klen = vpd_rd(handle, ep + 2);
                        int dlen;
                        char type;

                        ep += 3;

                        /*
                         * Look for the following properties:
                         *
                         * local-mac-address:
                         * -- VPD Instance 'I'
                         * -- VPD Type String 'B'
                         * -- property string == local-mac-address
                         *
                         * model:
                         * -- VPD Instance 'M'
                         * -- VPD Type String 'S'
                         * -- property string == model
                         *
                         * board-model:
                         * -- VPD Instance 'M'
                         * -- VPD Type String 'S'
                         * -- property string == board-model
                         *
                         * num-mac-addresses:
                         * -- VPD Instance 'I'
                         * -- VPD Type String 'B'
                         * -- property string == num-mac-addresses
                         *
                         * phy-type:
                         * -- VPD Instance 'I'
                         * -- VPD Type String 'S'
                         * -- property string == phy-type
                         *
                         * version:
                         * -- VPD Instance 'M'
                         * -- VPD Type String 'S'
                         * -- property string == version
                         */
                        if (vpd_rd(handle, ep) == 'M') {
                                type = vpd_rd(handle, ep + 3);
                                if (type == 'S') {
                                        dlen = vpd_rd(handle, ep + 4);
                                        if (npi_vpd_read_prop(handle, ep + 5,
                                            "model", dlen, vpdp->model)) {
                                                fd_flags |= FD_MODEL;
                                                goto next;
                                        }
                                        if (npi_vpd_read_prop(handle, ep + 5,
                                            "board-model", dlen,
                                            vpdp->bd_model)) {
                                                fd_flags |= FD_BD_MODEL;
                                                goto next;
                                        }
                                        if (npi_vpd_read_prop(handle, ep + 5,
                                            "version", dlen, vpdp->ver)) {
                                                fd_flags |= FD_FW_VERSION;
                                                goto next;
                                        }
                                }
                                goto next;
                        } else if (vpd_rd(handle, ep) == 'I') {
                                type = vpd_rd(handle, ep + 3);
                                if (type == 'B') {
                                        dlen = vpd_rd(handle, ep + 4);
                                        if (npi_vpd_read_prop(handle, ep + 5,
                                            "local-mac-address", dlen,
                                            (char *)(vpdp->mac_addr))) {
                                                fd_flags |= FD_MAC_ADDR;
                                                goto next;
                                        }
                                        if (npi_vpd_read_prop(handle, ep + 5,
                                            "num-mac-addresses", dlen,
                                            (char *)&(vpdp->num_macs))) {
                                                fd_flags |= FD_NUM_MACS;
                                        }
                                } else if (type == 'S') {
                                        dlen = vpd_rd(handle, ep + 4);
                                        if (npi_vpd_read_prop(handle, ep + 5,
                                            "phy-type", dlen,
                                            vpdp->phy_type)) {
                                                fd_flags |= FD_PHY_TYPE;
                                        }
                                }
                                goto next;
                        } else {
                                goto vpd_info_err;
                        }

next:
                        if ((fd_flags & FD_ALL) == FD_ALL)
                                goto vpd_success;
                        ep += klen;
                }
                i += len + 3;
        }

vpd_success:
        return (NPI_SUCCESS);

vpd_info_err:
        return (NPI_FAILURE);
}

static int
npi_vpd_read_prop(npi_handle_t handle, uint32_t ep, const char *prop, int len,
                char *val)
{
        int prop_len =  strlen(prop) + 1;
        int i;

        for (i = 0; i < prop_len; i++) {
                if (vpd_rd(handle, ep + i) != prop[i])
                        return (0);
        }

        ep += prop_len;

        for (i = 0; i < len; i++)
                val[i] = vpd_rd(handle, ep + i);
        return (1);
}