18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * AMD 10Gb Ethernet driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is available to you under your choice of the following two 58c2ecf20Sopenharmony_ci * licenses: 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * License 1: GPLv2 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Copyright (c) 2016 Advanced Micro Devices, Inc. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * This file is free software; you may copy, redistribute and/or modify 128c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by 138c2ecf20Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or (at 148c2ecf20Sopenharmony_ci * your option) any later version. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 178c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 188c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 198c2ecf20Sopenharmony_ci * General Public License for more details. 208c2ecf20Sopenharmony_ci * 218c2ecf20Sopenharmony_ci * You should have received a copy of the GNU General Public License 228c2ecf20Sopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * This file incorporates work covered by the following copyright and 258c2ecf20Sopenharmony_ci * permission notice: 268c2ecf20Sopenharmony_ci * The Synopsys DWC ETHER XGMAC Software Driver and documentation 278c2ecf20Sopenharmony_ci * (hereinafter "Software") is an unsupported proprietary work of Synopsys, 288c2ecf20Sopenharmony_ci * Inc. unless otherwise expressly agreed to in writing between Synopsys 298c2ecf20Sopenharmony_ci * and you. 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * The Software IS NOT an item of Licensed Software or Licensed Product 328c2ecf20Sopenharmony_ci * under any End User Software License Agreement or Agreement for Licensed 338c2ecf20Sopenharmony_ci * Product with Synopsys or any supplement thereto. Permission is hereby 348c2ecf20Sopenharmony_ci * granted, free of charge, to any person obtaining a copy of this software 358c2ecf20Sopenharmony_ci * annotated with this license and the Software, to deal in the Software 368c2ecf20Sopenharmony_ci * without restriction, including without limitation the rights to use, 378c2ecf20Sopenharmony_ci * copy, modify, merge, publish, distribute, sublicense, and/or sell copies 388c2ecf20Sopenharmony_ci * of the Software, and to permit persons to whom the Software is furnished 398c2ecf20Sopenharmony_ci * to do so, subject to the following conditions: 408c2ecf20Sopenharmony_ci * 418c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included 428c2ecf20Sopenharmony_ci * in all copies or substantial portions of the Software. 438c2ecf20Sopenharmony_ci * 448c2ecf20Sopenharmony_ci * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" 458c2ecf20Sopenharmony_ci * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 468c2ecf20Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 478c2ecf20Sopenharmony_ci * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 488c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 498c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 508c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 518c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 528c2ecf20Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 538c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 548c2ecf20Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGE. 558c2ecf20Sopenharmony_ci * 568c2ecf20Sopenharmony_ci * 578c2ecf20Sopenharmony_ci * License 2: Modified BSD 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci * Copyright (c) 2016 Advanced Micro Devices, Inc. 608c2ecf20Sopenharmony_ci * All rights reserved. 618c2ecf20Sopenharmony_ci * 628c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 638c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 648c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 658c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 668c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 678c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 688c2ecf20Sopenharmony_ci * documentation and/or other materials provided with the distribution. 698c2ecf20Sopenharmony_ci * * Neither the name of Advanced Micro Devices, Inc. nor the 708c2ecf20Sopenharmony_ci * names of its contributors may be used to endorse or promote products 718c2ecf20Sopenharmony_ci * derived from this software without specific prior written permission. 728c2ecf20Sopenharmony_ci * 738c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 748c2ecf20Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 758c2ecf20Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 768c2ecf20Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY 778c2ecf20Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 788c2ecf20Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 798c2ecf20Sopenharmony_ci * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 808c2ecf20Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 818c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 828c2ecf20Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 838c2ecf20Sopenharmony_ci * 848c2ecf20Sopenharmony_ci * This file incorporates work covered by the following copyright and 858c2ecf20Sopenharmony_ci * permission notice: 868c2ecf20Sopenharmony_ci * The Synopsys DWC ETHER XGMAC Software Driver and documentation 878c2ecf20Sopenharmony_ci * (hereinafter "Software") is an unsupported proprietary work of Synopsys, 888c2ecf20Sopenharmony_ci * Inc. unless otherwise expressly agreed to in writing between Synopsys 898c2ecf20Sopenharmony_ci * and you. 908c2ecf20Sopenharmony_ci * 918c2ecf20Sopenharmony_ci * The Software IS NOT an item of Licensed Software or Licensed Product 928c2ecf20Sopenharmony_ci * under any End User Software License Agreement or Agreement for Licensed 938c2ecf20Sopenharmony_ci * Product with Synopsys or any supplement thereto. Permission is hereby 948c2ecf20Sopenharmony_ci * granted, free of charge, to any person obtaining a copy of this software 958c2ecf20Sopenharmony_ci * annotated with this license and the Software, to deal in the Software 968c2ecf20Sopenharmony_ci * without restriction, including without limitation the rights to use, 978c2ecf20Sopenharmony_ci * copy, modify, merge, publish, distribute, sublicense, and/or sell copies 988c2ecf20Sopenharmony_ci * of the Software, and to permit persons to whom the Software is furnished 998c2ecf20Sopenharmony_ci * to do so, subject to the following conditions: 1008c2ecf20Sopenharmony_ci * 1018c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included 1028c2ecf20Sopenharmony_ci * in all copies or substantial portions of the Software. 1038c2ecf20Sopenharmony_ci * 1048c2ecf20Sopenharmony_ci * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" 1058c2ecf20Sopenharmony_ci * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 1068c2ecf20Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 1078c2ecf20Sopenharmony_ci * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 1088c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1098c2ecf20Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1108c2ecf20Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1118c2ecf20Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1128c2ecf20Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1138c2ecf20Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 1148c2ecf20Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGE. 1158c2ecf20Sopenharmony_ci */ 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci#include <linux/module.h> 1188c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 1198c2ecf20Sopenharmony_ci#include <linux/kmod.h> 1208c2ecf20Sopenharmony_ci#include <linux/delay.h> 1218c2ecf20Sopenharmony_ci#include <linux/completion.h> 1228c2ecf20Sopenharmony_ci#include <linux/mutex.h> 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci#include "xgbe.h" 1258c2ecf20Sopenharmony_ci#include "xgbe-common.h" 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci#define XGBE_ABORT_COUNT 500 1288c2ecf20Sopenharmony_ci#define XGBE_DISABLE_COUNT 1000 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci#define XGBE_STD_SPEED 1 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci#define XGBE_INTR_RX_FULL BIT(IC_RAW_INTR_STAT_RX_FULL_INDEX) 1338c2ecf20Sopenharmony_ci#define XGBE_INTR_TX_EMPTY BIT(IC_RAW_INTR_STAT_TX_EMPTY_INDEX) 1348c2ecf20Sopenharmony_ci#define XGBE_INTR_TX_ABRT BIT(IC_RAW_INTR_STAT_TX_ABRT_INDEX) 1358c2ecf20Sopenharmony_ci#define XGBE_INTR_STOP_DET BIT(IC_RAW_INTR_STAT_STOP_DET_INDEX) 1368c2ecf20Sopenharmony_ci#define XGBE_DEFAULT_INT_MASK (XGBE_INTR_RX_FULL | \ 1378c2ecf20Sopenharmony_ci XGBE_INTR_TX_EMPTY | \ 1388c2ecf20Sopenharmony_ci XGBE_INTR_TX_ABRT | \ 1398c2ecf20Sopenharmony_ci XGBE_INTR_STOP_DET) 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci#define XGBE_I2C_READ BIT(8) 1428c2ecf20Sopenharmony_ci#define XGBE_I2C_STOP BIT(9) 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_cistatic int xgbe_i2c_abort(struct xgbe_prv_data *pdata) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci unsigned int wait = XGBE_ABORT_COUNT; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci /* Must be enabled to recognize the abort request */ 1498c2ecf20Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, 1); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Issue the abort */ 1528c2ecf20Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_ENABLE, ABORT, 1); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci while (wait--) { 1558c2ecf20Sopenharmony_ci if (!XI2C_IOREAD_BITS(pdata, IC_ENABLE, ABORT)) 1568c2ecf20Sopenharmony_ci return 0; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci usleep_range(500, 600); 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci return -EBUSY; 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int xgbe_i2c_set_enable(struct xgbe_prv_data *pdata, bool enable) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned int wait = XGBE_DISABLE_COUNT; 1678c2ecf20Sopenharmony_ci unsigned int mode = enable ? 1 : 0; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci while (wait--) { 1708c2ecf20Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, mode); 1718c2ecf20Sopenharmony_ci if (XI2C_IOREAD_BITS(pdata, IC_ENABLE_STATUS, EN) == mode) 1728c2ecf20Sopenharmony_ci return 0; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci usleep_range(100, 110); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci return -EBUSY; 1788c2ecf20Sopenharmony_ci} 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_cistatic int xgbe_i2c_disable(struct xgbe_prv_data *pdata) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci unsigned int ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci ret = xgbe_i2c_set_enable(pdata, false); 1858c2ecf20Sopenharmony_ci if (ret) { 1868c2ecf20Sopenharmony_ci /* Disable failed, try an abort */ 1878c2ecf20Sopenharmony_ci ret = xgbe_i2c_abort(pdata); 1888c2ecf20Sopenharmony_ci if (ret) 1898c2ecf20Sopenharmony_ci return ret; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Abort succeeded, try to disable again */ 1928c2ecf20Sopenharmony_ci ret = xgbe_i2c_set_enable(pdata, false); 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return ret; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int xgbe_i2c_enable(struct xgbe_prv_data *pdata) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return xgbe_i2c_set_enable(pdata, true); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void xgbe_i2c_clear_all_interrupts(struct xgbe_prv_data *pdata) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci XI2C_IOREAD(pdata, IC_CLR_INTR); 2068c2ecf20Sopenharmony_ci} 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_cistatic void xgbe_i2c_disable_interrupts(struct xgbe_prv_data *pdata) 2098c2ecf20Sopenharmony_ci{ 2108c2ecf20Sopenharmony_ci XI2C_IOWRITE(pdata, IC_INTR_MASK, 0); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void xgbe_i2c_enable_interrupts(struct xgbe_prv_data *pdata) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci XI2C_IOWRITE(pdata, IC_INTR_MASK, XGBE_DEFAULT_INT_MASK); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic void xgbe_i2c_write(struct xgbe_prv_data *pdata) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 2218c2ecf20Sopenharmony_ci unsigned int tx_slots; 2228c2ecf20Sopenharmony_ci unsigned int cmd; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci /* Configured to never receive Rx overflows, so fill up Tx fifo */ 2258c2ecf20Sopenharmony_ci tx_slots = pdata->i2c.tx_fifo_size - XI2C_IOREAD(pdata, IC_TXFLR); 2268c2ecf20Sopenharmony_ci while (tx_slots && state->tx_len) { 2278c2ecf20Sopenharmony_ci if (state->op->cmd == XGBE_I2C_CMD_READ) 2288c2ecf20Sopenharmony_ci cmd = XGBE_I2C_READ; 2298c2ecf20Sopenharmony_ci else 2308c2ecf20Sopenharmony_ci cmd = *state->tx_buf++; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci if (state->tx_len == 1) 2338c2ecf20Sopenharmony_ci XI2C_SET_BITS(cmd, IC_DATA_CMD, STOP, 1); 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci XI2C_IOWRITE(pdata, IC_DATA_CMD, cmd); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci tx_slots--; 2388c2ecf20Sopenharmony_ci state->tx_len--; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* No more Tx operations, so ignore TX_EMPTY and return */ 2428c2ecf20Sopenharmony_ci if (!state->tx_len) 2438c2ecf20Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_INTR_MASK, TX_EMPTY, 0); 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic void xgbe_i2c_read(struct xgbe_prv_data *pdata) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 2498c2ecf20Sopenharmony_ci unsigned int rx_slots; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci /* Anything to be read? */ 2528c2ecf20Sopenharmony_ci if (state->op->cmd != XGBE_I2C_CMD_READ) 2538c2ecf20Sopenharmony_ci return; 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci rx_slots = XI2C_IOREAD(pdata, IC_RXFLR); 2568c2ecf20Sopenharmony_ci while (rx_slots && state->rx_len) { 2578c2ecf20Sopenharmony_ci *state->rx_buf++ = XI2C_IOREAD(pdata, IC_DATA_CMD); 2588c2ecf20Sopenharmony_ci state->rx_len--; 2598c2ecf20Sopenharmony_ci rx_slots--; 2608c2ecf20Sopenharmony_ci } 2618c2ecf20Sopenharmony_ci} 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_cistatic void xgbe_i2c_clear_isr_interrupts(struct xgbe_prv_data *pdata, 2648c2ecf20Sopenharmony_ci unsigned int isr) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (isr & XGBE_INTR_TX_ABRT) { 2698c2ecf20Sopenharmony_ci state->tx_abort_source = XI2C_IOREAD(pdata, IC_TX_ABRT_SOURCE); 2708c2ecf20Sopenharmony_ci XI2C_IOREAD(pdata, IC_CLR_TX_ABRT); 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci if (isr & XGBE_INTR_STOP_DET) 2748c2ecf20Sopenharmony_ci XI2C_IOREAD(pdata, IC_CLR_STOP_DET); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void xgbe_i2c_isr_task(struct tasklet_struct *t) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci struct xgbe_prv_data *pdata = from_tasklet(pdata, t, tasklet_i2c); 2808c2ecf20Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 2818c2ecf20Sopenharmony_ci unsigned int isr; 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci isr = XI2C_IOREAD(pdata, IC_RAW_INTR_STAT); 2848c2ecf20Sopenharmony_ci if (!isr) 2858c2ecf20Sopenharmony_ci goto reissue_check; 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci netif_dbg(pdata, intr, pdata->netdev, 2888c2ecf20Sopenharmony_ci "I2C interrupt received: status=%#010x\n", isr); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci xgbe_i2c_clear_isr_interrupts(pdata, isr); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci if (isr & XGBE_INTR_TX_ABRT) { 2938c2ecf20Sopenharmony_ci netif_dbg(pdata, link, pdata->netdev, 2948c2ecf20Sopenharmony_ci "I2C TX_ABRT received (%#010x) for target %#04x\n", 2958c2ecf20Sopenharmony_ci state->tx_abort_source, state->op->target); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci state->ret = -EIO; 3008c2ecf20Sopenharmony_ci goto out; 3018c2ecf20Sopenharmony_ci } 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci /* Check for data in the Rx fifo */ 3048c2ecf20Sopenharmony_ci xgbe_i2c_read(pdata); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci /* Fill up the Tx fifo next */ 3078c2ecf20Sopenharmony_ci xgbe_i2c_write(pdata); 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ciout: 3108c2ecf20Sopenharmony_ci /* Complete on an error or STOP condition */ 3118c2ecf20Sopenharmony_ci if (state->ret || XI2C_GET_BITS(isr, IC_RAW_INTR_STAT, STOP_DET)) 3128c2ecf20Sopenharmony_ci complete(&pdata->i2c_complete); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_cireissue_check: 3158c2ecf20Sopenharmony_ci /* Reissue interrupt if status is not clear */ 3168c2ecf20Sopenharmony_ci if (pdata->vdata->irq_reissue_support) 3178c2ecf20Sopenharmony_ci XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 2); 3188c2ecf20Sopenharmony_ci} 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_cistatic irqreturn_t xgbe_i2c_isr(int irq, void *data) 3218c2ecf20Sopenharmony_ci{ 3228c2ecf20Sopenharmony_ci struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci if (pdata->isr_as_tasklet) 3258c2ecf20Sopenharmony_ci tasklet_schedule(&pdata->tasklet_i2c); 3268c2ecf20Sopenharmony_ci else 3278c2ecf20Sopenharmony_ci xgbe_i2c_isr_task(&pdata->tasklet_i2c); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_cistatic void xgbe_i2c_set_mode(struct xgbe_prv_data *pdata) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci unsigned int reg; 3358c2ecf20Sopenharmony_ci 3368c2ecf20Sopenharmony_ci reg = XI2C_IOREAD(pdata, IC_CON); 3378c2ecf20Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, MASTER_MODE, 1); 3388c2ecf20Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, SLAVE_DISABLE, 1); 3398c2ecf20Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, RESTART_EN, 1); 3408c2ecf20Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, SPEED, XGBE_STD_SPEED); 3418c2ecf20Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, RX_FIFO_FULL_HOLD, 1); 3428c2ecf20Sopenharmony_ci XI2C_IOWRITE(pdata, IC_CON, reg); 3438c2ecf20Sopenharmony_ci} 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_cistatic void xgbe_i2c_get_features(struct xgbe_prv_data *pdata) 3468c2ecf20Sopenharmony_ci{ 3478c2ecf20Sopenharmony_ci struct xgbe_i2c *i2c = &pdata->i2c; 3488c2ecf20Sopenharmony_ci unsigned int reg; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci reg = XI2C_IOREAD(pdata, IC_COMP_PARAM_1); 3518c2ecf20Sopenharmony_ci i2c->max_speed_mode = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, 3528c2ecf20Sopenharmony_ci MAX_SPEED_MODE); 3538c2ecf20Sopenharmony_ci i2c->rx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, 3548c2ecf20Sopenharmony_ci RX_BUFFER_DEPTH); 3558c2ecf20Sopenharmony_ci i2c->tx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, 3568c2ecf20Sopenharmony_ci TX_BUFFER_DEPTH); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (netif_msg_probe(pdata)) 3598c2ecf20Sopenharmony_ci dev_dbg(pdata->dev, "I2C features: %s=%u, %s=%u, %s=%u\n", 3608c2ecf20Sopenharmony_ci "MAX_SPEED_MODE", i2c->max_speed_mode, 3618c2ecf20Sopenharmony_ci "RX_BUFFER_DEPTH", i2c->rx_fifo_size, 3628c2ecf20Sopenharmony_ci "TX_BUFFER_DEPTH", i2c->tx_fifo_size); 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic void xgbe_i2c_set_target(struct xgbe_prv_data *pdata, unsigned int addr) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci XI2C_IOWRITE(pdata, IC_TAR, addr); 3688c2ecf20Sopenharmony_ci} 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic irqreturn_t xgbe_i2c_combined_isr(struct xgbe_prv_data *pdata) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci xgbe_i2c_isr_task(&pdata->tasklet_i2c); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3758c2ecf20Sopenharmony_ci} 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_cistatic int xgbe_i2c_xfer(struct xgbe_prv_data *pdata, struct xgbe_i2c_op *op) 3788c2ecf20Sopenharmony_ci{ 3798c2ecf20Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 3808c2ecf20Sopenharmony_ci int ret; 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci mutex_lock(&pdata->i2c_mutex); 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci reinit_completion(&pdata->i2c_complete); 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci ret = xgbe_i2c_disable(pdata); 3878c2ecf20Sopenharmony_ci if (ret) { 3888c2ecf20Sopenharmony_ci netdev_err(pdata->netdev, "failed to disable i2c master\n"); 3898c2ecf20Sopenharmony_ci goto unlock; 3908c2ecf20Sopenharmony_ci } 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci xgbe_i2c_set_target(pdata, op->target); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci memset(state, 0, sizeof(*state)); 3958c2ecf20Sopenharmony_ci state->op = op; 3968c2ecf20Sopenharmony_ci state->tx_len = op->len; 3978c2ecf20Sopenharmony_ci state->tx_buf = op->buf; 3988c2ecf20Sopenharmony_ci state->rx_len = op->len; 3998c2ecf20Sopenharmony_ci state->rx_buf = op->buf; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci xgbe_i2c_clear_all_interrupts(pdata); 4028c2ecf20Sopenharmony_ci ret = xgbe_i2c_enable(pdata); 4038c2ecf20Sopenharmony_ci if (ret) { 4048c2ecf20Sopenharmony_ci netdev_err(pdata->netdev, "failed to enable i2c master\n"); 4058c2ecf20Sopenharmony_ci goto unlock; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci /* Enabling the interrupts will cause the TX FIFO empty interrupt to 4098c2ecf20Sopenharmony_ci * fire and begin to process the command via the ISR. 4108c2ecf20Sopenharmony_ci */ 4118c2ecf20Sopenharmony_ci xgbe_i2c_enable_interrupts(pdata); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (!wait_for_completion_timeout(&pdata->i2c_complete, HZ)) { 4148c2ecf20Sopenharmony_ci netdev_err(pdata->netdev, "i2c operation timed out\n"); 4158c2ecf20Sopenharmony_ci ret = -ETIMEDOUT; 4168c2ecf20Sopenharmony_ci goto disable; 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci ret = state->ret; 4208c2ecf20Sopenharmony_ci if (ret) { 4218c2ecf20Sopenharmony_ci if (state->tx_abort_source & IC_TX_ABRT_7B_ADDR_NOACK) 4228c2ecf20Sopenharmony_ci ret = -ENOTCONN; 4238c2ecf20Sopenharmony_ci else if (state->tx_abort_source & IC_TX_ABRT_ARB_LOST) 4248c2ecf20Sopenharmony_ci ret = -EAGAIN; 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cidisable: 4288c2ecf20Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 4298c2ecf20Sopenharmony_ci xgbe_i2c_disable(pdata); 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ciunlock: 4328c2ecf20Sopenharmony_ci mutex_unlock(&pdata->i2c_mutex); 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci return ret; 4358c2ecf20Sopenharmony_ci} 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_cistatic void xgbe_i2c_stop(struct xgbe_prv_data *pdata) 4388c2ecf20Sopenharmony_ci{ 4398c2ecf20Sopenharmony_ci if (!pdata->i2c.started) 4408c2ecf20Sopenharmony_ci return; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci netif_dbg(pdata, link, pdata->netdev, "stopping I2C\n"); 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci pdata->i2c.started = 0; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 4478c2ecf20Sopenharmony_ci xgbe_i2c_disable(pdata); 4488c2ecf20Sopenharmony_ci xgbe_i2c_clear_all_interrupts(pdata); 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (pdata->dev_irq != pdata->i2c_irq) { 4518c2ecf20Sopenharmony_ci devm_free_irq(pdata->dev, pdata->i2c_irq, pdata); 4528c2ecf20Sopenharmony_ci tasklet_kill(&pdata->tasklet_i2c); 4538c2ecf20Sopenharmony_ci } 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_cistatic int xgbe_i2c_start(struct xgbe_prv_data *pdata) 4578c2ecf20Sopenharmony_ci{ 4588c2ecf20Sopenharmony_ci int ret; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if (pdata->i2c.started) 4618c2ecf20Sopenharmony_ci return 0; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci netif_dbg(pdata, link, pdata->netdev, "starting I2C\n"); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci /* If we have a separate I2C irq, enable it */ 4668c2ecf20Sopenharmony_ci if (pdata->dev_irq != pdata->i2c_irq) { 4678c2ecf20Sopenharmony_ci tasklet_setup(&pdata->tasklet_i2c, xgbe_i2c_isr_task); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci ret = devm_request_irq(pdata->dev, pdata->i2c_irq, 4708c2ecf20Sopenharmony_ci xgbe_i2c_isr, 0, pdata->i2c_name, 4718c2ecf20Sopenharmony_ci pdata); 4728c2ecf20Sopenharmony_ci if (ret) { 4738c2ecf20Sopenharmony_ci netdev_err(pdata->netdev, "i2c irq request failed\n"); 4748c2ecf20Sopenharmony_ci return ret; 4758c2ecf20Sopenharmony_ci } 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci pdata->i2c.started = 1; 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci} 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int xgbe_i2c_init(struct xgbe_prv_data *pdata) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci int ret; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci ret = xgbe_i2c_disable(pdata); 4908c2ecf20Sopenharmony_ci if (ret) { 4918c2ecf20Sopenharmony_ci dev_err(pdata->dev, "failed to disable i2c master\n"); 4928c2ecf20Sopenharmony_ci return ret; 4938c2ecf20Sopenharmony_ci } 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci xgbe_i2c_get_features(pdata); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci xgbe_i2c_set_mode(pdata); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci xgbe_i2c_clear_all_interrupts(pdata); 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci return 0; 5028c2ecf20Sopenharmony_ci} 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_civoid xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *i2c_if) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci i2c_if->i2c_init = xgbe_i2c_init; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci i2c_if->i2c_start = xgbe_i2c_start; 5098c2ecf20Sopenharmony_ci i2c_if->i2c_stop = xgbe_i2c_stop; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci i2c_if->i2c_xfer = xgbe_i2c_xfer; 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci i2c_if->i2c_isr = xgbe_i2c_combined_isr; 5148c2ecf20Sopenharmony_ci} 515