18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2015, 2016 Intel Corporation. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 168c2ecf20Sopenharmony_ci * General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * BSD LICENSE 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 228c2ecf20Sopenharmony_ci * are met: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 258c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 268c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 278c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 288c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 298c2ecf20Sopenharmony_ci * distribution. 308c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 318c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 328c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/delay.h> 498c2ecf20Sopenharmony_ci#include <linux/pci.h> 508c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#include "hfi.h" 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci/* for the given bus number, return the CSR for reading an i2c line */ 558c2ecf20Sopenharmony_cistatic inline u32 i2c_in_csr(u32 bus_num) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci return bus_num ? ASIC_QSFP2_IN : ASIC_QSFP1_IN; 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* for the given bus number, return the CSR for writing an i2c line */ 618c2ecf20Sopenharmony_cistatic inline u32 i2c_oe_csr(u32 bus_num) 628c2ecf20Sopenharmony_ci{ 638c2ecf20Sopenharmony_ci return bus_num ? ASIC_QSFP2_OE : ASIC_QSFP1_OE; 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic void hfi1_setsda(void *data, int state) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 698c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = bus->controlling_dd; 708c2ecf20Sopenharmony_ci u64 reg; 718c2ecf20Sopenharmony_ci u32 target_oe; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci target_oe = i2c_oe_csr(bus->num); 748c2ecf20Sopenharmony_ci reg = read_csr(dd, target_oe); 758c2ecf20Sopenharmony_ci /* 768c2ecf20Sopenharmony_ci * The OE bit value is inverted and connected to the pin. When 778c2ecf20Sopenharmony_ci * OE is 0 the pin is left to be pulled up, when the OE is 1 788c2ecf20Sopenharmony_ci * the pin is driven low. This matches the "open drain" or "open 798c2ecf20Sopenharmony_ci * collector" convention. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci if (state) 828c2ecf20Sopenharmony_ci reg &= ~QSFP_HFI0_I2CDAT; 838c2ecf20Sopenharmony_ci else 848c2ecf20Sopenharmony_ci reg |= QSFP_HFI0_I2CDAT; 858c2ecf20Sopenharmony_ci write_csr(dd, target_oe, reg); 868c2ecf20Sopenharmony_ci /* do a read to force the write into the chip */ 878c2ecf20Sopenharmony_ci (void)read_csr(dd, target_oe); 888c2ecf20Sopenharmony_ci} 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_cistatic void hfi1_setscl(void *data, int state) 918c2ecf20Sopenharmony_ci{ 928c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 938c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = bus->controlling_dd; 948c2ecf20Sopenharmony_ci u64 reg; 958c2ecf20Sopenharmony_ci u32 target_oe; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci target_oe = i2c_oe_csr(bus->num); 988c2ecf20Sopenharmony_ci reg = read_csr(dd, target_oe); 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * The OE bit value is inverted and connected to the pin. When 1018c2ecf20Sopenharmony_ci * OE is 0 the pin is left to be pulled up, when the OE is 1 1028c2ecf20Sopenharmony_ci * the pin is driven low. This matches the "open drain" or "open 1038c2ecf20Sopenharmony_ci * collector" convention. 1048c2ecf20Sopenharmony_ci */ 1058c2ecf20Sopenharmony_ci if (state) 1068c2ecf20Sopenharmony_ci reg &= ~QSFP_HFI0_I2CCLK; 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci reg |= QSFP_HFI0_I2CCLK; 1098c2ecf20Sopenharmony_ci write_csr(dd, target_oe, reg); 1108c2ecf20Sopenharmony_ci /* do a read to force the write into the chip */ 1118c2ecf20Sopenharmony_ci (void)read_csr(dd, target_oe); 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int hfi1_getsda(void *data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 1178c2ecf20Sopenharmony_ci u64 reg; 1188c2ecf20Sopenharmony_ci u32 target_in; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci hfi1_setsda(data, 1); /* clear OE so we do not pull line down */ 1218c2ecf20Sopenharmony_ci udelay(2); /* 1us pull up + 250ns hold */ 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci target_in = i2c_in_csr(bus->num); 1248c2ecf20Sopenharmony_ci reg = read_csr(bus->controlling_dd, target_in); 1258c2ecf20Sopenharmony_ci return !!(reg & QSFP_HFI0_I2CDAT); 1268c2ecf20Sopenharmony_ci} 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic int hfi1_getscl(void *data) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus = (struct hfi1_i2c_bus *)data; 1318c2ecf20Sopenharmony_ci u64 reg; 1328c2ecf20Sopenharmony_ci u32 target_in; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci hfi1_setscl(data, 1); /* clear OE so we do not pull line down */ 1358c2ecf20Sopenharmony_ci udelay(2); /* 1us pull up + 250ns hold */ 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci target_in = i2c_in_csr(bus->num); 1388c2ecf20Sopenharmony_ci reg = read_csr(bus->controlling_dd, target_in); 1398c2ecf20Sopenharmony_ci return !!(reg & QSFP_HFI0_I2CCLK); 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci/* 1438c2ecf20Sopenharmony_ci * Allocate and initialize the given i2c bus number. 1448c2ecf20Sopenharmony_ci * Returns NULL on failure. 1458c2ecf20Sopenharmony_ci */ 1468c2ecf20Sopenharmony_cistatic struct hfi1_i2c_bus *init_i2c_bus(struct hfi1_devdata *dd, 1478c2ecf20Sopenharmony_ci struct hfi1_asic_data *ad, int num) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus; 1508c2ecf20Sopenharmony_ci int ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci bus = kzalloc(sizeof(*bus), GFP_KERNEL); 1538c2ecf20Sopenharmony_ci if (!bus) 1548c2ecf20Sopenharmony_ci return NULL; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci bus->controlling_dd = dd; 1578c2ecf20Sopenharmony_ci bus->num = num; /* our bus number */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci bus->algo.setsda = hfi1_setsda; 1608c2ecf20Sopenharmony_ci bus->algo.setscl = hfi1_setscl; 1618c2ecf20Sopenharmony_ci bus->algo.getsda = hfi1_getsda; 1628c2ecf20Sopenharmony_ci bus->algo.getscl = hfi1_getscl; 1638c2ecf20Sopenharmony_ci bus->algo.udelay = 5; 1648c2ecf20Sopenharmony_ci bus->algo.timeout = usecs_to_jiffies(100000); 1658c2ecf20Sopenharmony_ci bus->algo.data = bus; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci bus->adapter.owner = THIS_MODULE; 1688c2ecf20Sopenharmony_ci bus->adapter.algo_data = &bus->algo; 1698c2ecf20Sopenharmony_ci bus->adapter.dev.parent = &dd->pcidev->dev; 1708c2ecf20Sopenharmony_ci snprintf(bus->adapter.name, sizeof(bus->adapter.name), 1718c2ecf20Sopenharmony_ci "hfi1_i2c%d", num); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci ret = i2c_bit_add_bus(&bus->adapter); 1748c2ecf20Sopenharmony_ci if (ret) { 1758c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: unable to add i2c bus %d, err %d\n", 1768c2ecf20Sopenharmony_ci __func__, num, ret); 1778c2ecf20Sopenharmony_ci kfree(bus); 1788c2ecf20Sopenharmony_ci return NULL; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return bus; 1828c2ecf20Sopenharmony_ci} 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci/* 1858c2ecf20Sopenharmony_ci * Initialize i2c buses. 1868c2ecf20Sopenharmony_ci * Return 0 on success, -errno on error. 1878c2ecf20Sopenharmony_ci */ 1888c2ecf20Sopenharmony_ciint set_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci ad->i2c_bus0 = init_i2c_bus(dd, ad, 0); 1918c2ecf20Sopenharmony_ci ad->i2c_bus1 = init_i2c_bus(dd, ad, 1); 1928c2ecf20Sopenharmony_ci if (!ad->i2c_bus0 || !ad->i2c_bus1) 1938c2ecf20Sopenharmony_ci return -ENOMEM; 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci}; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic void clean_i2c_bus(struct hfi1_i2c_bus *bus) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci if (bus) { 2008c2ecf20Sopenharmony_ci i2c_del_adapter(&bus->adapter); 2018c2ecf20Sopenharmony_ci kfree(bus); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_civoid clean_up_i2c(struct hfi1_devdata *dd, struct hfi1_asic_data *ad) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci if (!ad) 2088c2ecf20Sopenharmony_ci return; 2098c2ecf20Sopenharmony_ci clean_i2c_bus(ad->i2c_bus0); 2108c2ecf20Sopenharmony_ci ad->i2c_bus0 = NULL; 2118c2ecf20Sopenharmony_ci clean_i2c_bus(ad->i2c_bus1); 2128c2ecf20Sopenharmony_ci ad->i2c_bus1 = NULL; 2138c2ecf20Sopenharmony_ci} 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_cistatic int i2c_bus_write(struct hfi1_devdata *dd, struct hfi1_i2c_bus *i2c, 2168c2ecf20Sopenharmony_ci u8 slave_addr, int offset, int offset_size, 2178c2ecf20Sopenharmony_ci u8 *data, u16 len) 2188c2ecf20Sopenharmony_ci{ 2198c2ecf20Sopenharmony_ci int ret; 2208c2ecf20Sopenharmony_ci int num_msgs; 2218c2ecf20Sopenharmony_ci u8 offset_bytes[2]; 2228c2ecf20Sopenharmony_ci struct i2c_msg msgs[2]; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci switch (offset_size) { 2258c2ecf20Sopenharmony_ci case 0: 2268c2ecf20Sopenharmony_ci num_msgs = 1; 2278c2ecf20Sopenharmony_ci msgs[0].addr = slave_addr; 2288c2ecf20Sopenharmony_ci msgs[0].flags = 0; 2298c2ecf20Sopenharmony_ci msgs[0].len = len; 2308c2ecf20Sopenharmony_ci msgs[0].buf = data; 2318c2ecf20Sopenharmony_ci break; 2328c2ecf20Sopenharmony_ci case 2: 2338c2ecf20Sopenharmony_ci offset_bytes[1] = (offset >> 8) & 0xff; 2348c2ecf20Sopenharmony_ci fallthrough; 2358c2ecf20Sopenharmony_ci case 1: 2368c2ecf20Sopenharmony_ci num_msgs = 2; 2378c2ecf20Sopenharmony_ci offset_bytes[0] = offset & 0xff; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci msgs[0].addr = slave_addr; 2408c2ecf20Sopenharmony_ci msgs[0].flags = 0; 2418c2ecf20Sopenharmony_ci msgs[0].len = offset_size; 2428c2ecf20Sopenharmony_ci msgs[0].buf = offset_bytes; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci msgs[1].addr = slave_addr; 2458c2ecf20Sopenharmony_ci msgs[1].flags = I2C_M_NOSTART, 2468c2ecf20Sopenharmony_ci msgs[1].len = len; 2478c2ecf20Sopenharmony_ci msgs[1].buf = data; 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci default: 2508c2ecf20Sopenharmony_ci return -EINVAL; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci i2c->controlling_dd = dd; 2548c2ecf20Sopenharmony_ci ret = i2c_transfer(&i2c->adapter, msgs, num_msgs); 2558c2ecf20Sopenharmony_ci if (ret != num_msgs) { 2568c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: bus %d, i2c slave 0x%x, offset 0x%x, len 0x%x; write failed, ret %d\n", 2578c2ecf20Sopenharmony_ci __func__, i2c->num, slave_addr, offset, len, ret); 2588c2ecf20Sopenharmony_ci return ret < 0 ? ret : -EIO; 2598c2ecf20Sopenharmony_ci } 2608c2ecf20Sopenharmony_ci return 0; 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic int i2c_bus_read(struct hfi1_devdata *dd, struct hfi1_i2c_bus *bus, 2648c2ecf20Sopenharmony_ci u8 slave_addr, int offset, int offset_size, 2658c2ecf20Sopenharmony_ci u8 *data, u16 len) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci int ret; 2688c2ecf20Sopenharmony_ci int num_msgs; 2698c2ecf20Sopenharmony_ci u8 offset_bytes[2]; 2708c2ecf20Sopenharmony_ci struct i2c_msg msgs[2]; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci switch (offset_size) { 2738c2ecf20Sopenharmony_ci case 0: 2748c2ecf20Sopenharmony_ci num_msgs = 1; 2758c2ecf20Sopenharmony_ci msgs[0].addr = slave_addr; 2768c2ecf20Sopenharmony_ci msgs[0].flags = I2C_M_RD; 2778c2ecf20Sopenharmony_ci msgs[0].len = len; 2788c2ecf20Sopenharmony_ci msgs[0].buf = data; 2798c2ecf20Sopenharmony_ci break; 2808c2ecf20Sopenharmony_ci case 2: 2818c2ecf20Sopenharmony_ci offset_bytes[1] = (offset >> 8) & 0xff; 2828c2ecf20Sopenharmony_ci fallthrough; 2838c2ecf20Sopenharmony_ci case 1: 2848c2ecf20Sopenharmony_ci num_msgs = 2; 2858c2ecf20Sopenharmony_ci offset_bytes[0] = offset & 0xff; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci msgs[0].addr = slave_addr; 2888c2ecf20Sopenharmony_ci msgs[0].flags = 0; 2898c2ecf20Sopenharmony_ci msgs[0].len = offset_size; 2908c2ecf20Sopenharmony_ci msgs[0].buf = offset_bytes; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci msgs[1].addr = slave_addr; 2938c2ecf20Sopenharmony_ci msgs[1].flags = I2C_M_RD, 2948c2ecf20Sopenharmony_ci msgs[1].len = len; 2958c2ecf20Sopenharmony_ci msgs[1].buf = data; 2968c2ecf20Sopenharmony_ci break; 2978c2ecf20Sopenharmony_ci default: 2988c2ecf20Sopenharmony_ci return -EINVAL; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci bus->controlling_dd = dd; 3028c2ecf20Sopenharmony_ci ret = i2c_transfer(&bus->adapter, msgs, num_msgs); 3038c2ecf20Sopenharmony_ci if (ret != num_msgs) { 3048c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: bus %d, i2c slave 0x%x, offset 0x%x, len 0x%x; read failed, ret %d\n", 3058c2ecf20Sopenharmony_ci __func__, bus->num, slave_addr, offset, len, ret); 3068c2ecf20Sopenharmony_ci return ret < 0 ? ret : -EIO; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci return 0; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci/* 3128c2ecf20Sopenharmony_ci * Raw i2c write. No set-up or lock checking. 3138c2ecf20Sopenharmony_ci * 3148c2ecf20Sopenharmony_ci * Return 0 on success, -errno on error. 3158c2ecf20Sopenharmony_ci */ 3168c2ecf20Sopenharmony_cistatic int __i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, 3178c2ecf20Sopenharmony_ci int offset, void *bp, int len) 3188c2ecf20Sopenharmony_ci{ 3198c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 3208c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus; 3218c2ecf20Sopenharmony_ci u8 slave_addr; 3228c2ecf20Sopenharmony_ci int offset_size; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci bus = target ? dd->asic_data->i2c_bus1 : dd->asic_data->i2c_bus0; 3258c2ecf20Sopenharmony_ci slave_addr = (i2c_addr & 0xff) >> 1; /* convert to 7-bit addr */ 3268c2ecf20Sopenharmony_ci offset_size = (i2c_addr >> 8) & 0x3; 3278c2ecf20Sopenharmony_ci return i2c_bus_write(dd, bus, slave_addr, offset, offset_size, bp, len); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci/* 3318c2ecf20Sopenharmony_ci * Caller must hold the i2c chain resource. 3328c2ecf20Sopenharmony_ci * 3338c2ecf20Sopenharmony_ci * Return number of bytes written, or -errno. 3348c2ecf20Sopenharmony_ci */ 3358c2ecf20Sopenharmony_ciint i2c_write(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset, 3368c2ecf20Sopenharmony_ci void *bp, int len) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci int ret; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 3418c2ecf20Sopenharmony_ci return -EACCES; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci ret = __i2c_write(ppd, target, i2c_addr, offset, bp, len); 3448c2ecf20Sopenharmony_ci if (ret) 3458c2ecf20Sopenharmony_ci return ret; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci return len; 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci/* 3518c2ecf20Sopenharmony_ci * Raw i2c read. No set-up or lock checking. 3528c2ecf20Sopenharmony_ci * 3538c2ecf20Sopenharmony_ci * Return 0 on success, -errno on error. 3548c2ecf20Sopenharmony_ci */ 3558c2ecf20Sopenharmony_cistatic int __i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, 3568c2ecf20Sopenharmony_ci int offset, void *bp, int len) 3578c2ecf20Sopenharmony_ci{ 3588c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 3598c2ecf20Sopenharmony_ci struct hfi1_i2c_bus *bus; 3608c2ecf20Sopenharmony_ci u8 slave_addr; 3618c2ecf20Sopenharmony_ci int offset_size; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci bus = target ? dd->asic_data->i2c_bus1 : dd->asic_data->i2c_bus0; 3648c2ecf20Sopenharmony_ci slave_addr = (i2c_addr & 0xff) >> 1; /* convert to 7-bit addr */ 3658c2ecf20Sopenharmony_ci offset_size = (i2c_addr >> 8) & 0x3; 3668c2ecf20Sopenharmony_ci return i2c_bus_read(dd, bus, slave_addr, offset, offset_size, bp, len); 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * Caller must hold the i2c chain resource. 3718c2ecf20Sopenharmony_ci * 3728c2ecf20Sopenharmony_ci * Return number of bytes read, or -errno. 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ciint i2c_read(struct hfi1_pportdata *ppd, u32 target, int i2c_addr, int offset, 3758c2ecf20Sopenharmony_ci void *bp, int len) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci int ret; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 3808c2ecf20Sopenharmony_ci return -EACCES; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = __i2c_read(ppd, target, i2c_addr, offset, bp, len); 3838c2ecf20Sopenharmony_ci if (ret) 3848c2ecf20Sopenharmony_ci return ret; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return len; 3878c2ecf20Sopenharmony_ci} 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci/* 3908c2ecf20Sopenharmony_ci * Write page n, offset m of QSFP memory as defined by SFF 8636 3918c2ecf20Sopenharmony_ci * by writing @addr = ((256 * n) + m) 3928c2ecf20Sopenharmony_ci * 3938c2ecf20Sopenharmony_ci * Caller must hold the i2c chain resource. 3948c2ecf20Sopenharmony_ci * 3958c2ecf20Sopenharmony_ci * Return number of bytes written or -errno. 3968c2ecf20Sopenharmony_ci */ 3978c2ecf20Sopenharmony_ciint qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 3988c2ecf20Sopenharmony_ci int len) 3998c2ecf20Sopenharmony_ci{ 4008c2ecf20Sopenharmony_ci int count = 0; 4018c2ecf20Sopenharmony_ci int offset; 4028c2ecf20Sopenharmony_ci int nwrite; 4038c2ecf20Sopenharmony_ci int ret = 0; 4048c2ecf20Sopenharmony_ci u8 page; 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 4078c2ecf20Sopenharmony_ci return -EACCES; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci while (count < len) { 4108c2ecf20Sopenharmony_ci /* 4118c2ecf20Sopenharmony_ci * Set the qsfp page based on a zero-based address 4128c2ecf20Sopenharmony_ci * and a page size of QSFP_PAGESIZE bytes. 4138c2ecf20Sopenharmony_ci */ 4148c2ecf20Sopenharmony_ci page = (u8)(addr / QSFP_PAGESIZE); 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 4178c2ecf20Sopenharmony_ci QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1); 4188c2ecf20Sopenharmony_ci /* QSFPs require a 5-10msec delay after write operations */ 4198c2ecf20Sopenharmony_ci mdelay(5); 4208c2ecf20Sopenharmony_ci if (ret) { 4218c2ecf20Sopenharmony_ci hfi1_dev_porterr(ppd->dd, ppd->port, 4228c2ecf20Sopenharmony_ci "QSFP chain %d can't write QSFP_PAGE_SELECT_BYTE: %d\n", 4238c2ecf20Sopenharmony_ci target, ret); 4248c2ecf20Sopenharmony_ci break; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci offset = addr % QSFP_PAGESIZE; 4288c2ecf20Sopenharmony_ci nwrite = len - count; 4298c2ecf20Sopenharmony_ci /* truncate write to boundary if crossing boundary */ 4308c2ecf20Sopenharmony_ci if (((addr % QSFP_RW_BOUNDARY) + nwrite) > QSFP_RW_BOUNDARY) 4318c2ecf20Sopenharmony_ci nwrite = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 4348c2ecf20Sopenharmony_ci offset, bp + count, nwrite); 4358c2ecf20Sopenharmony_ci /* QSFPs require a 5-10msec delay after write operations */ 4368c2ecf20Sopenharmony_ci mdelay(5); 4378c2ecf20Sopenharmony_ci if (ret) /* stop on error */ 4388c2ecf20Sopenharmony_ci break; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci count += nwrite; 4418c2ecf20Sopenharmony_ci addr += nwrite; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (ret < 0) 4458c2ecf20Sopenharmony_ci return ret; 4468c2ecf20Sopenharmony_ci return count; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* 4508c2ecf20Sopenharmony_ci * Perform a stand-alone single QSFP write. Acquire the resource, do the 4518c2ecf20Sopenharmony_ci * write, then release the resource. 4528c2ecf20Sopenharmony_ci */ 4538c2ecf20Sopenharmony_ciint one_qsfp_write(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 4548c2ecf20Sopenharmony_ci int len) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 4578c2ecf20Sopenharmony_ci u32 resource = qsfp_resource(dd); 4588c2ecf20Sopenharmony_ci int ret; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci ret = acquire_chip_resource(dd, resource, QSFP_WAIT); 4618c2ecf20Sopenharmony_ci if (ret) 4628c2ecf20Sopenharmony_ci return ret; 4638c2ecf20Sopenharmony_ci ret = qsfp_write(ppd, target, addr, bp, len); 4648c2ecf20Sopenharmony_ci release_chip_resource(dd, resource); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci/* 4708c2ecf20Sopenharmony_ci * Access page n, offset m of QSFP memory as defined by SFF 8636 4718c2ecf20Sopenharmony_ci * by reading @addr = ((256 * n) + m) 4728c2ecf20Sopenharmony_ci * 4738c2ecf20Sopenharmony_ci * Caller must hold the i2c chain resource. 4748c2ecf20Sopenharmony_ci * 4758c2ecf20Sopenharmony_ci * Return the number of bytes read or -errno. 4768c2ecf20Sopenharmony_ci */ 4778c2ecf20Sopenharmony_ciint qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 4788c2ecf20Sopenharmony_ci int len) 4798c2ecf20Sopenharmony_ci{ 4808c2ecf20Sopenharmony_ci int count = 0; 4818c2ecf20Sopenharmony_ci int offset; 4828c2ecf20Sopenharmony_ci int nread; 4838c2ecf20Sopenharmony_ci int ret = 0; 4848c2ecf20Sopenharmony_ci u8 page; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci if (!check_chip_resource(ppd->dd, i2c_target(target), __func__)) 4878c2ecf20Sopenharmony_ci return -EACCES; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci while (count < len) { 4908c2ecf20Sopenharmony_ci /* 4918c2ecf20Sopenharmony_ci * Set the qsfp page based on a zero-based address 4928c2ecf20Sopenharmony_ci * and a page size of QSFP_PAGESIZE bytes. 4938c2ecf20Sopenharmony_ci */ 4948c2ecf20Sopenharmony_ci page = (u8)(addr / QSFP_PAGESIZE); 4958c2ecf20Sopenharmony_ci ret = __i2c_write(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 4968c2ecf20Sopenharmony_ci QSFP_PAGE_SELECT_BYTE_OFFS, &page, 1); 4978c2ecf20Sopenharmony_ci /* QSFPs require a 5-10msec delay after write operations */ 4988c2ecf20Sopenharmony_ci mdelay(5); 4998c2ecf20Sopenharmony_ci if (ret) { 5008c2ecf20Sopenharmony_ci hfi1_dev_porterr(ppd->dd, ppd->port, 5018c2ecf20Sopenharmony_ci "QSFP chain %d can't write QSFP_PAGE_SELECT_BYTE: %d\n", 5028c2ecf20Sopenharmony_ci target, ret); 5038c2ecf20Sopenharmony_ci break; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci offset = addr % QSFP_PAGESIZE; 5078c2ecf20Sopenharmony_ci nread = len - count; 5088c2ecf20Sopenharmony_ci /* truncate read to boundary if crossing boundary */ 5098c2ecf20Sopenharmony_ci if (((addr % QSFP_RW_BOUNDARY) + nread) > QSFP_RW_BOUNDARY) 5108c2ecf20Sopenharmony_ci nread = QSFP_RW_BOUNDARY - (addr % QSFP_RW_BOUNDARY); 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci ret = __i2c_read(ppd, target, QSFP_DEV | QSFP_OFFSET_SIZE, 5138c2ecf20Sopenharmony_ci offset, bp + count, nread); 5148c2ecf20Sopenharmony_ci if (ret) /* stop on error */ 5158c2ecf20Sopenharmony_ci break; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci count += nread; 5188c2ecf20Sopenharmony_ci addr += nread; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (ret < 0) 5228c2ecf20Sopenharmony_ci return ret; 5238c2ecf20Sopenharmony_ci return count; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_ci/* 5278c2ecf20Sopenharmony_ci * Perform a stand-alone single QSFP read. Acquire the resource, do the 5288c2ecf20Sopenharmony_ci * read, then release the resource. 5298c2ecf20Sopenharmony_ci */ 5308c2ecf20Sopenharmony_ciint one_qsfp_read(struct hfi1_pportdata *ppd, u32 target, int addr, void *bp, 5318c2ecf20Sopenharmony_ci int len) 5328c2ecf20Sopenharmony_ci{ 5338c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 5348c2ecf20Sopenharmony_ci u32 resource = qsfp_resource(dd); 5358c2ecf20Sopenharmony_ci int ret; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci ret = acquire_chip_resource(dd, resource, QSFP_WAIT); 5388c2ecf20Sopenharmony_ci if (ret) 5398c2ecf20Sopenharmony_ci return ret; 5408c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, addr, bp, len); 5418c2ecf20Sopenharmony_ci release_chip_resource(dd, resource); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci return ret; 5448c2ecf20Sopenharmony_ci} 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/* 5478c2ecf20Sopenharmony_ci * This function caches the QSFP memory range in 128 byte chunks. 5488c2ecf20Sopenharmony_ci * As an example, the next byte after address 255 is byte 128 from 5498c2ecf20Sopenharmony_ci * upper page 01H (if existing) rather than byte 0 from lower page 00H. 5508c2ecf20Sopenharmony_ci * Access page n, offset m of QSFP memory as defined by SFF 8636 5518c2ecf20Sopenharmony_ci * in the cache by reading byte ((128 * n) + m) 5528c2ecf20Sopenharmony_ci * The calls to qsfp_{read,write} in this function correctly handle the 5538c2ecf20Sopenharmony_ci * address map difference between this mapping and the mapping implemented 5548c2ecf20Sopenharmony_ci * by those functions 5558c2ecf20Sopenharmony_ci * 5568c2ecf20Sopenharmony_ci * The caller must be holding the QSFP i2c chain resource. 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ciint refresh_qsfp_cache(struct hfi1_pportdata *ppd, struct qsfp_data *cp) 5598c2ecf20Sopenharmony_ci{ 5608c2ecf20Sopenharmony_ci u32 target = ppd->dd->hfi1_id; 5618c2ecf20Sopenharmony_ci int ret; 5628c2ecf20Sopenharmony_ci unsigned long flags; 5638c2ecf20Sopenharmony_ci u8 *cache = &cp->cache[0]; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* ensure sane contents on invalid reads, for cable swaps */ 5668c2ecf20Sopenharmony_ci memset(cache, 0, (QSFP_MAX_NUM_PAGES * 128)); 5678c2ecf20Sopenharmony_ci spin_lock_irqsave(&ppd->qsfp_info.qsfp_lock, flags); 5688c2ecf20Sopenharmony_ci ppd->qsfp_info.cache_valid = 0; 5698c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags); 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci if (!qsfp_mod_present(ppd)) { 5728c2ecf20Sopenharmony_ci ret = -ENODEV; 5738c2ecf20Sopenharmony_ci goto bail; 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 0, cache, QSFP_PAGESIZE); 5778c2ecf20Sopenharmony_ci if (ret != QSFP_PAGESIZE) { 5788c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, 5798c2ecf20Sopenharmony_ci "%s: Page 0 read failed, expected %d, got %d\n", 5808c2ecf20Sopenharmony_ci __func__, QSFP_PAGESIZE, ret); 5818c2ecf20Sopenharmony_ci goto bail; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci /* Is paging enabled? */ 5858c2ecf20Sopenharmony_ci if (!(cache[2] & 4)) { 5868c2ecf20Sopenharmony_ci /* Paging enabled, page 03 required */ 5878c2ecf20Sopenharmony_ci if ((cache[195] & 0xC0) == 0xC0) { 5888c2ecf20Sopenharmony_ci /* all */ 5898c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 384, cache + 256, 128); 5908c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 5918c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 5928c2ecf20Sopenharmony_ci goto bail; 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 640, cache + 384, 128); 5958c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 5968c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 5978c2ecf20Sopenharmony_ci goto bail; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 6008c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 6018c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 6028c2ecf20Sopenharmony_ci goto bail; 6038c2ecf20Sopenharmony_ci } 6048c2ecf20Sopenharmony_ci } else if ((cache[195] & 0x80) == 0x80) { 6058c2ecf20Sopenharmony_ci /* only page 2 and 3 */ 6068c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 640, cache + 384, 128); 6078c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 6088c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 6098c2ecf20Sopenharmony_ci goto bail; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 6128c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 6138c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 6148c2ecf20Sopenharmony_ci goto bail; 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci } else if ((cache[195] & 0x40) == 0x40) { 6178c2ecf20Sopenharmony_ci /* only page 1 and 3 */ 6188c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 384, cache + 256, 128); 6198c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 6208c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 6218c2ecf20Sopenharmony_ci goto bail; 6228c2ecf20Sopenharmony_ci } 6238c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 6248c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 6258c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 6268c2ecf20Sopenharmony_ci goto bail; 6278c2ecf20Sopenharmony_ci } 6288c2ecf20Sopenharmony_ci } else { 6298c2ecf20Sopenharmony_ci /* only page 3 */ 6308c2ecf20Sopenharmony_ci ret = qsfp_read(ppd, target, 896, cache + 512, 128); 6318c2ecf20Sopenharmony_ci if (ret <= 0 || ret != 128) { 6328c2ecf20Sopenharmony_ci dd_dev_info(ppd->dd, "%s failed\n", __func__); 6338c2ecf20Sopenharmony_ci goto bail; 6348c2ecf20Sopenharmony_ci } 6358c2ecf20Sopenharmony_ci } 6368c2ecf20Sopenharmony_ci } 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci spin_lock_irqsave(&ppd->qsfp_info.qsfp_lock, flags); 6398c2ecf20Sopenharmony_ci ppd->qsfp_info.cache_valid = 1; 6408c2ecf20Sopenharmony_ci ppd->qsfp_info.cache_refresh_required = 0; 6418c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ppd->qsfp_info.qsfp_lock, flags); 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci return 0; 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_cibail: 6468c2ecf20Sopenharmony_ci memset(cache, 0, (QSFP_MAX_NUM_PAGES * 128)); 6478c2ecf20Sopenharmony_ci return ret; 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_ciconst char * const hfi1_qsfp_devtech[16] = { 6518c2ecf20Sopenharmony_ci "850nm VCSEL", "1310nm VCSEL", "1550nm VCSEL", "1310nm FP", 6528c2ecf20Sopenharmony_ci "1310nm DFB", "1550nm DFB", "1310nm EML", "1550nm EML", 6538c2ecf20Sopenharmony_ci "Cu Misc", "1490nm DFB", "Cu NoEq", "Cu Eq", 6548c2ecf20Sopenharmony_ci "Undef", "Cu Active BothEq", "Cu FarEq", "Cu NearEq" 6558c2ecf20Sopenharmony_ci}; 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci#define QSFP_DUMP_CHUNK 16 /* Holds longest string */ 6588c2ecf20Sopenharmony_ci#define QSFP_DEFAULT_HDR_CNT 224 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci#define QSFP_PWR(pbyte) (((pbyte) >> 6) & 3) 6618c2ecf20Sopenharmony_ci#define QSFP_HIGH_PWR(pbyte) ((pbyte) & 3) 6628c2ecf20Sopenharmony_ci/* For use with QSFP_HIGH_PWR macro */ 6638c2ecf20Sopenharmony_ci#define QSFP_HIGH_PWR_UNUSED 0 /* Bits [1:0] = 00 implies low power module */ 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* 6668c2ecf20Sopenharmony_ci * Takes power class byte [Page 00 Byte 129] in SFF 8636 6678c2ecf20Sopenharmony_ci * Returns power class as integer (1 through 7, per SFF 8636 rev 2.4) 6688c2ecf20Sopenharmony_ci */ 6698c2ecf20Sopenharmony_ciint get_qsfp_power_class(u8 power_byte) 6708c2ecf20Sopenharmony_ci{ 6718c2ecf20Sopenharmony_ci if (QSFP_HIGH_PWR(power_byte) == QSFP_HIGH_PWR_UNUSED) 6728c2ecf20Sopenharmony_ci /* power classes count from 1, their bit encodings from 0 */ 6738c2ecf20Sopenharmony_ci return (QSFP_PWR(power_byte) + 1); 6748c2ecf20Sopenharmony_ci /* 6758c2ecf20Sopenharmony_ci * 00 in the high power classes stands for unused, bringing 6768c2ecf20Sopenharmony_ci * balance to the off-by-1 offset above, we add 4 here to 6778c2ecf20Sopenharmony_ci * account for the difference between the low and high power 6788c2ecf20Sopenharmony_ci * groups 6798c2ecf20Sopenharmony_ci */ 6808c2ecf20Sopenharmony_ci return (QSFP_HIGH_PWR(power_byte) + 4); 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ciint qsfp_mod_present(struct hfi1_pportdata *ppd) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = ppd->dd; 6868c2ecf20Sopenharmony_ci u64 reg; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci reg = read_csr(dd, dd->hfi1_id ? ASIC_QSFP2_IN : ASIC_QSFP1_IN); 6898c2ecf20Sopenharmony_ci return !(reg & QSFP_HFI0_MODPRST_N); 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * This function maps QSFP memory addresses in 128 byte chunks in the following 6948c2ecf20Sopenharmony_ci * fashion per the CableInfo SMA query definition in the IBA 1.3 spec/OPA Gen 1 6958c2ecf20Sopenharmony_ci * spec 6968c2ecf20Sopenharmony_ci * For addr 000-127, lower page 00h 6978c2ecf20Sopenharmony_ci * For addr 128-255, upper page 00h 6988c2ecf20Sopenharmony_ci * For addr 256-383, upper page 01h 6998c2ecf20Sopenharmony_ci * For addr 384-511, upper page 02h 7008c2ecf20Sopenharmony_ci * For addr 512-639, upper page 03h 7018c2ecf20Sopenharmony_ci * 7028c2ecf20Sopenharmony_ci * For addresses beyond this range, it returns the invalid range of data buffer 7038c2ecf20Sopenharmony_ci * set to 0. 7048c2ecf20Sopenharmony_ci * For upper pages that are optional, if they are not valid, returns the 7058c2ecf20Sopenharmony_ci * particular range of bytes in the data buffer set to 0. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ciint get_cable_info(struct hfi1_devdata *dd, u32 port_num, u32 addr, u32 len, 7088c2ecf20Sopenharmony_ci u8 *data) 7098c2ecf20Sopenharmony_ci{ 7108c2ecf20Sopenharmony_ci struct hfi1_pportdata *ppd; 7118c2ecf20Sopenharmony_ci u32 excess_len = len; 7128c2ecf20Sopenharmony_ci int ret = 0, offset = 0; 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (port_num > dd->num_pports || port_num < 1) { 7158c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: Invalid port number %d\n", 7168c2ecf20Sopenharmony_ci __func__, port_num); 7178c2ecf20Sopenharmony_ci ret = -EINVAL; 7188c2ecf20Sopenharmony_ci goto set_zeroes; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci ppd = dd->pport + (port_num - 1); 7228c2ecf20Sopenharmony_ci if (!qsfp_mod_present(ppd)) { 7238c2ecf20Sopenharmony_ci ret = -ENODEV; 7248c2ecf20Sopenharmony_ci goto set_zeroes; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (!ppd->qsfp_info.cache_valid) { 7288c2ecf20Sopenharmony_ci ret = -EINVAL; 7298c2ecf20Sopenharmony_ci goto set_zeroes; 7308c2ecf20Sopenharmony_ci } 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (addr >= (QSFP_MAX_NUM_PAGES * 128)) { 7338c2ecf20Sopenharmony_ci ret = -ERANGE; 7348c2ecf20Sopenharmony_ci goto set_zeroes; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci if ((addr + len) > (QSFP_MAX_NUM_PAGES * 128)) { 7388c2ecf20Sopenharmony_ci excess_len = (addr + len) - (QSFP_MAX_NUM_PAGES * 128); 7398c2ecf20Sopenharmony_ci memcpy(data, &ppd->qsfp_info.cache[addr], (len - excess_len)); 7408c2ecf20Sopenharmony_ci data += (len - excess_len); 7418c2ecf20Sopenharmony_ci goto set_zeroes; 7428c2ecf20Sopenharmony_ci } 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci memcpy(data, &ppd->qsfp_info.cache[addr], len); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci if (addr <= QSFP_MONITOR_VAL_END && 7478c2ecf20Sopenharmony_ci (addr + len) >= QSFP_MONITOR_VAL_START) { 7488c2ecf20Sopenharmony_ci /* Overlap with the dynamic channel monitor range */ 7498c2ecf20Sopenharmony_ci if (addr < QSFP_MONITOR_VAL_START) { 7508c2ecf20Sopenharmony_ci if (addr + len <= QSFP_MONITOR_VAL_END) 7518c2ecf20Sopenharmony_ci len = addr + len - QSFP_MONITOR_VAL_START; 7528c2ecf20Sopenharmony_ci else 7538c2ecf20Sopenharmony_ci len = QSFP_MONITOR_RANGE; 7548c2ecf20Sopenharmony_ci offset = QSFP_MONITOR_VAL_START - addr; 7558c2ecf20Sopenharmony_ci addr = QSFP_MONITOR_VAL_START; 7568c2ecf20Sopenharmony_ci } else if (addr == QSFP_MONITOR_VAL_START) { 7578c2ecf20Sopenharmony_ci offset = 0; 7588c2ecf20Sopenharmony_ci if (addr + len > QSFP_MONITOR_VAL_END) 7598c2ecf20Sopenharmony_ci len = QSFP_MONITOR_RANGE; 7608c2ecf20Sopenharmony_ci } else { 7618c2ecf20Sopenharmony_ci offset = 0; 7628c2ecf20Sopenharmony_ci if (addr + len > QSFP_MONITOR_VAL_END) 7638c2ecf20Sopenharmony_ci len = QSFP_MONITOR_VAL_END - addr + 1; 7648c2ecf20Sopenharmony_ci } 7658c2ecf20Sopenharmony_ci /* Refresh the values of the dynamic monitors from the cable */ 7668c2ecf20Sopenharmony_ci ret = one_qsfp_read(ppd, dd->hfi1_id, addr, data + offset, len); 7678c2ecf20Sopenharmony_ci if (ret != len) { 7688c2ecf20Sopenharmony_ci ret = -EAGAIN; 7698c2ecf20Sopenharmony_ci goto set_zeroes; 7708c2ecf20Sopenharmony_ci } 7718c2ecf20Sopenharmony_ci } 7728c2ecf20Sopenharmony_ci 7738c2ecf20Sopenharmony_ci return 0; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ciset_zeroes: 7768c2ecf20Sopenharmony_ci memset(data, 0, excess_len); 7778c2ecf20Sopenharmony_ci return ret; 7788c2ecf20Sopenharmony_ci} 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_cistatic const char *pwr_codes[8] = {"N/AW", 7818c2ecf20Sopenharmony_ci "1.5W", 7828c2ecf20Sopenharmony_ci "2.0W", 7838c2ecf20Sopenharmony_ci "2.5W", 7848c2ecf20Sopenharmony_ci "3.5W", 7858c2ecf20Sopenharmony_ci "4.0W", 7868c2ecf20Sopenharmony_ci "4.5W", 7878c2ecf20Sopenharmony_ci "5.0W" 7888c2ecf20Sopenharmony_ci }; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ciint qsfp_dump(struct hfi1_pportdata *ppd, char *buf, int len) 7918c2ecf20Sopenharmony_ci{ 7928c2ecf20Sopenharmony_ci u8 *cache = &ppd->qsfp_info.cache[0]; 7938c2ecf20Sopenharmony_ci u8 bin_buff[QSFP_DUMP_CHUNK]; 7948c2ecf20Sopenharmony_ci char lenstr[6]; 7958c2ecf20Sopenharmony_ci int sofar; 7968c2ecf20Sopenharmony_ci int bidx = 0; 7978c2ecf20Sopenharmony_ci u8 *atten = &cache[QSFP_ATTEN_OFFS]; 7988c2ecf20Sopenharmony_ci u8 *vendor_oui = &cache[QSFP_VOUI_OFFS]; 7998c2ecf20Sopenharmony_ci u8 power_byte = 0; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci sofar = 0; 8028c2ecf20Sopenharmony_ci lenstr[0] = ' '; 8038c2ecf20Sopenharmony_ci lenstr[1] = '\0'; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (ppd->qsfp_info.cache_valid) { 8068c2ecf20Sopenharmony_ci if (QSFP_IS_CU(cache[QSFP_MOD_TECH_OFFS])) 8078c2ecf20Sopenharmony_ci snprintf(lenstr, sizeof(lenstr), "%dM ", 8088c2ecf20Sopenharmony_ci cache[QSFP_MOD_LEN_OFFS]); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci power_byte = cache[QSFP_MOD_PWR_OFFS]; 8118c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "PWR:%.3sW\n", 8128c2ecf20Sopenharmony_ci pwr_codes[get_qsfp_power_class(power_byte)]); 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "TECH:%s%s\n", 8158c2ecf20Sopenharmony_ci lenstr, 8168c2ecf20Sopenharmony_ci hfi1_qsfp_devtech[(cache[QSFP_MOD_TECH_OFFS]) >> 4]); 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Vendor:%.*s\n", 8198c2ecf20Sopenharmony_ci QSFP_VEND_LEN, &cache[QSFP_VEND_OFFS]); 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "OUI:%06X\n", 8228c2ecf20Sopenharmony_ci QSFP_OUI(vendor_oui)); 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Part#:%.*s\n", 8258c2ecf20Sopenharmony_ci QSFP_PN_LEN, &cache[QSFP_PN_OFFS]); 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Rev:%.*s\n", 8288c2ecf20Sopenharmony_ci QSFP_REV_LEN, &cache[QSFP_REV_OFFS]); 8298c2ecf20Sopenharmony_ci 8308c2ecf20Sopenharmony_ci if (QSFP_IS_CU(cache[QSFP_MOD_TECH_OFFS])) 8318c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, 8328c2ecf20Sopenharmony_ci "Atten:%d, %d\n", 8338c2ecf20Sopenharmony_ci QSFP_ATTEN_SDR(atten), 8348c2ecf20Sopenharmony_ci QSFP_ATTEN_DDR(atten)); 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Serial:%.*s\n", 8378c2ecf20Sopenharmony_ci QSFP_SN_LEN, &cache[QSFP_SN_OFFS]); 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Date:%.*s\n", 8408c2ecf20Sopenharmony_ci QSFP_DATE_LEN, &cache[QSFP_DATE_OFFS]); 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "Lot:%.*s\n", 8438c2ecf20Sopenharmony_ci QSFP_LOT_LEN, &cache[QSFP_LOT_OFFS]); 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci while (bidx < QSFP_DEFAULT_HDR_CNT) { 8468c2ecf20Sopenharmony_ci int iidx; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci memcpy(bin_buff, &cache[bidx], QSFP_DUMP_CHUNK); 8498c2ecf20Sopenharmony_ci for (iidx = 0; iidx < QSFP_DUMP_CHUNK; ++iidx) { 8508c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, 8518c2ecf20Sopenharmony_ci " %02X", bin_buff[iidx]); 8528c2ecf20Sopenharmony_ci } 8538c2ecf20Sopenharmony_ci sofar += scnprintf(buf + sofar, len - sofar, "\n"); 8548c2ecf20Sopenharmony_ci bidx += QSFP_DUMP_CHUNK; 8558c2ecf20Sopenharmony_ci } 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci return sofar; 8588c2ecf20Sopenharmony_ci} 859