162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * AMD 10Gb Ethernet driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This file is available to you under your choice of the following two 562306a36Sopenharmony_ci * licenses: 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * License 1: GPLv2 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Copyright (c) 2016 Advanced Micro Devices, Inc. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This file is free software; you may copy, redistribute and/or modify 1262306a36Sopenharmony_ci * it under the terms of the GNU General Public License as published by 1362306a36Sopenharmony_ci * the Free Software Foundation, either version 2 of the License, or (at 1462306a36Sopenharmony_ci * your option) any later version. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * This file is distributed in the hope that it will be useful, but 1762306a36Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 1862306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1962306a36Sopenharmony_ci * General Public License for more details. 2062306a36Sopenharmony_ci * 2162306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License 2262306a36Sopenharmony_ci * along with this program. If not, see <http://www.gnu.org/licenses/>. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * This file incorporates work covered by the following copyright and 2562306a36Sopenharmony_ci * permission notice: 2662306a36Sopenharmony_ci * The Synopsys DWC ETHER XGMAC Software Driver and documentation 2762306a36Sopenharmony_ci * (hereinafter "Software") is an unsupported proprietary work of Synopsys, 2862306a36Sopenharmony_ci * Inc. unless otherwise expressly agreed to in writing between Synopsys 2962306a36Sopenharmony_ci * and you. 3062306a36Sopenharmony_ci * 3162306a36Sopenharmony_ci * The Software IS NOT an item of Licensed Software or Licensed Product 3262306a36Sopenharmony_ci * under any End User Software License Agreement or Agreement for Licensed 3362306a36Sopenharmony_ci * Product with Synopsys or any supplement thereto. Permission is hereby 3462306a36Sopenharmony_ci * granted, free of charge, to any person obtaining a copy of this software 3562306a36Sopenharmony_ci * annotated with this license and the Software, to deal in the Software 3662306a36Sopenharmony_ci * without restriction, including without limitation the rights to use, 3762306a36Sopenharmony_ci * copy, modify, merge, publish, distribute, sublicense, and/or sell copies 3862306a36Sopenharmony_ci * of the Software, and to permit persons to whom the Software is furnished 3962306a36Sopenharmony_ci * to do so, subject to the following conditions: 4062306a36Sopenharmony_ci * 4162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included 4262306a36Sopenharmony_ci * in all copies or substantial portions of the Software. 4362306a36Sopenharmony_ci * 4462306a36Sopenharmony_ci * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" 4562306a36Sopenharmony_ci * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 4662306a36Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 4762306a36Sopenharmony_ci * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 4862306a36Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 4962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 5062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 5162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 5262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 5362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 5462306a36Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGE. 5562306a36Sopenharmony_ci * 5662306a36Sopenharmony_ci * 5762306a36Sopenharmony_ci * License 2: Modified BSD 5862306a36Sopenharmony_ci * 5962306a36Sopenharmony_ci * Copyright (c) 2016 Advanced Micro Devices, Inc. 6062306a36Sopenharmony_ci * All rights reserved. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 6362306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met: 6462306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 6562306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 6662306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copyright 6762306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in the 6862306a36Sopenharmony_ci * documentation and/or other materials provided with the distribution. 6962306a36Sopenharmony_ci * * Neither the name of Advanced Micro Devices, Inc. nor the 7062306a36Sopenharmony_ci * names of its contributors may be used to endorse or promote products 7162306a36Sopenharmony_ci * derived from this software without specific prior written permission. 7262306a36Sopenharmony_ci * 7362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 7462306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 7562306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 7662306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY 7762306a36Sopenharmony_ci * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 7862306a36Sopenharmony_ci * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 7962306a36Sopenharmony_ci * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 8062306a36Sopenharmony_ci * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 8162306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 8262306a36Sopenharmony_ci * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 8362306a36Sopenharmony_ci * 8462306a36Sopenharmony_ci * This file incorporates work covered by the following copyright and 8562306a36Sopenharmony_ci * permission notice: 8662306a36Sopenharmony_ci * The Synopsys DWC ETHER XGMAC Software Driver and documentation 8762306a36Sopenharmony_ci * (hereinafter "Software") is an unsupported proprietary work of Synopsys, 8862306a36Sopenharmony_ci * Inc. unless otherwise expressly agreed to in writing between Synopsys 8962306a36Sopenharmony_ci * and you. 9062306a36Sopenharmony_ci * 9162306a36Sopenharmony_ci * The Software IS NOT an item of Licensed Software or Licensed Product 9262306a36Sopenharmony_ci * under any End User Software License Agreement or Agreement for Licensed 9362306a36Sopenharmony_ci * Product with Synopsys or any supplement thereto. Permission is hereby 9462306a36Sopenharmony_ci * granted, free of charge, to any person obtaining a copy of this software 9562306a36Sopenharmony_ci * annotated with this license and the Software, to deal in the Software 9662306a36Sopenharmony_ci * without restriction, including without limitation the rights to use, 9762306a36Sopenharmony_ci * copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9862306a36Sopenharmony_ci * of the Software, and to permit persons to whom the Software is furnished 9962306a36Sopenharmony_ci * to do so, subject to the following conditions: 10062306a36Sopenharmony_ci * 10162306a36Sopenharmony_ci * The above copyright notice and this permission notice shall be included 10262306a36Sopenharmony_ci * in all copies or substantial portions of the Software. 10362306a36Sopenharmony_ci * 10462306a36Sopenharmony_ci * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" 10562306a36Sopenharmony_ci * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 10662306a36Sopenharmony_ci * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 10762306a36Sopenharmony_ci * PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 10862306a36Sopenharmony_ci * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 10962306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 11062306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 11162306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 11262306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 11362306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 11462306a36Sopenharmony_ci * THE POSSIBILITY OF SUCH DAMAGE. 11562306a36Sopenharmony_ci */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci#include <linux/module.h> 11862306a36Sopenharmony_ci#include <linux/interrupt.h> 11962306a36Sopenharmony_ci#include <linux/kmod.h> 12062306a36Sopenharmony_ci#include <linux/delay.h> 12162306a36Sopenharmony_ci#include <linux/completion.h> 12262306a36Sopenharmony_ci#include <linux/mutex.h> 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci#include "xgbe.h" 12562306a36Sopenharmony_ci#include "xgbe-common.h" 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define XGBE_ABORT_COUNT 500 12862306a36Sopenharmony_ci#define XGBE_DISABLE_COUNT 1000 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci#define XGBE_STD_SPEED 1 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci#define XGBE_INTR_RX_FULL BIT(IC_RAW_INTR_STAT_RX_FULL_INDEX) 13362306a36Sopenharmony_ci#define XGBE_INTR_TX_EMPTY BIT(IC_RAW_INTR_STAT_TX_EMPTY_INDEX) 13462306a36Sopenharmony_ci#define XGBE_INTR_TX_ABRT BIT(IC_RAW_INTR_STAT_TX_ABRT_INDEX) 13562306a36Sopenharmony_ci#define XGBE_INTR_STOP_DET BIT(IC_RAW_INTR_STAT_STOP_DET_INDEX) 13662306a36Sopenharmony_ci#define XGBE_DEFAULT_INT_MASK (XGBE_INTR_RX_FULL | \ 13762306a36Sopenharmony_ci XGBE_INTR_TX_EMPTY | \ 13862306a36Sopenharmony_ci XGBE_INTR_TX_ABRT | \ 13962306a36Sopenharmony_ci XGBE_INTR_STOP_DET) 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci#define XGBE_I2C_READ BIT(8) 14262306a36Sopenharmony_ci#define XGBE_I2C_STOP BIT(9) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int xgbe_i2c_abort(struct xgbe_prv_data *pdata) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci unsigned int wait = XGBE_ABORT_COUNT; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* Must be enabled to recognize the abort request */ 14962306a36Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, 1); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Issue the abort */ 15262306a36Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_ENABLE, ABORT, 1); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci while (wait--) { 15562306a36Sopenharmony_ci if (!XI2C_IOREAD_BITS(pdata, IC_ENABLE, ABORT)) 15662306a36Sopenharmony_ci return 0; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci usleep_range(500, 600); 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci return -EBUSY; 16262306a36Sopenharmony_ci} 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_cistatic int xgbe_i2c_set_enable(struct xgbe_prv_data *pdata, bool enable) 16562306a36Sopenharmony_ci{ 16662306a36Sopenharmony_ci unsigned int wait = XGBE_DISABLE_COUNT; 16762306a36Sopenharmony_ci unsigned int mode = enable ? 1 : 0; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci while (wait--) { 17062306a36Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, mode); 17162306a36Sopenharmony_ci if (XI2C_IOREAD_BITS(pdata, IC_ENABLE_STATUS, EN) == mode) 17262306a36Sopenharmony_ci return 0; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci usleep_range(100, 110); 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci return -EBUSY; 17862306a36Sopenharmony_ci} 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_cistatic int xgbe_i2c_disable(struct xgbe_prv_data *pdata) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci unsigned int ret; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci ret = xgbe_i2c_set_enable(pdata, false); 18562306a36Sopenharmony_ci if (ret) { 18662306a36Sopenharmony_ci /* Disable failed, try an abort */ 18762306a36Sopenharmony_ci ret = xgbe_i2c_abort(pdata); 18862306a36Sopenharmony_ci if (ret) 18962306a36Sopenharmony_ci return ret; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci /* Abort succeeded, try to disable again */ 19262306a36Sopenharmony_ci ret = xgbe_i2c_set_enable(pdata, false); 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return ret; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int xgbe_i2c_enable(struct xgbe_prv_data *pdata) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci return xgbe_i2c_set_enable(pdata, true); 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void xgbe_i2c_clear_all_interrupts(struct xgbe_prv_data *pdata) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci XI2C_IOREAD(pdata, IC_CLR_INTR); 20662306a36Sopenharmony_ci} 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_cistatic void xgbe_i2c_disable_interrupts(struct xgbe_prv_data *pdata) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci XI2C_IOWRITE(pdata, IC_INTR_MASK, 0); 21162306a36Sopenharmony_ci} 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_cistatic void xgbe_i2c_enable_interrupts(struct xgbe_prv_data *pdata) 21462306a36Sopenharmony_ci{ 21562306a36Sopenharmony_ci XI2C_IOWRITE(pdata, IC_INTR_MASK, XGBE_DEFAULT_INT_MASK); 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic void xgbe_i2c_write(struct xgbe_prv_data *pdata) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 22162306a36Sopenharmony_ci unsigned int tx_slots; 22262306a36Sopenharmony_ci unsigned int cmd; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci /* Configured to never receive Rx overflows, so fill up Tx fifo */ 22562306a36Sopenharmony_ci tx_slots = pdata->i2c.tx_fifo_size - XI2C_IOREAD(pdata, IC_TXFLR); 22662306a36Sopenharmony_ci while (tx_slots && state->tx_len) { 22762306a36Sopenharmony_ci if (state->op->cmd == XGBE_I2C_CMD_READ) 22862306a36Sopenharmony_ci cmd = XGBE_I2C_READ; 22962306a36Sopenharmony_ci else 23062306a36Sopenharmony_ci cmd = *state->tx_buf++; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (state->tx_len == 1) 23362306a36Sopenharmony_ci XI2C_SET_BITS(cmd, IC_DATA_CMD, STOP, 1); 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci XI2C_IOWRITE(pdata, IC_DATA_CMD, cmd); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci tx_slots--; 23862306a36Sopenharmony_ci state->tx_len--; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci /* No more Tx operations, so ignore TX_EMPTY and return */ 24262306a36Sopenharmony_ci if (!state->tx_len) 24362306a36Sopenharmony_ci XI2C_IOWRITE_BITS(pdata, IC_INTR_MASK, TX_EMPTY, 0); 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void xgbe_i2c_read(struct xgbe_prv_data *pdata) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 24962306a36Sopenharmony_ci unsigned int rx_slots; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* Anything to be read? */ 25262306a36Sopenharmony_ci if (state->op->cmd != XGBE_I2C_CMD_READ) 25362306a36Sopenharmony_ci return; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci rx_slots = XI2C_IOREAD(pdata, IC_RXFLR); 25662306a36Sopenharmony_ci while (rx_slots && state->rx_len) { 25762306a36Sopenharmony_ci *state->rx_buf++ = XI2C_IOREAD(pdata, IC_DATA_CMD); 25862306a36Sopenharmony_ci state->rx_len--; 25962306a36Sopenharmony_ci rx_slots--; 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cistatic void xgbe_i2c_clear_isr_interrupts(struct xgbe_prv_data *pdata, 26462306a36Sopenharmony_ci unsigned int isr) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (isr & XGBE_INTR_TX_ABRT) { 26962306a36Sopenharmony_ci state->tx_abort_source = XI2C_IOREAD(pdata, IC_TX_ABRT_SOURCE); 27062306a36Sopenharmony_ci XI2C_IOREAD(pdata, IC_CLR_TX_ABRT); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (isr & XGBE_INTR_STOP_DET) 27462306a36Sopenharmony_ci XI2C_IOREAD(pdata, IC_CLR_STOP_DET); 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void xgbe_i2c_isr_task(struct tasklet_struct *t) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct xgbe_prv_data *pdata = from_tasklet(pdata, t, tasklet_i2c); 28062306a36Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 28162306a36Sopenharmony_ci unsigned int isr; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci isr = XI2C_IOREAD(pdata, IC_RAW_INTR_STAT); 28462306a36Sopenharmony_ci if (!isr) 28562306a36Sopenharmony_ci goto reissue_check; 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci netif_dbg(pdata, intr, pdata->netdev, 28862306a36Sopenharmony_ci "I2C interrupt received: status=%#010x\n", isr); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci xgbe_i2c_clear_isr_interrupts(pdata, isr); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (isr & XGBE_INTR_TX_ABRT) { 29362306a36Sopenharmony_ci netif_dbg(pdata, link, pdata->netdev, 29462306a36Sopenharmony_ci "I2C TX_ABRT received (%#010x) for target %#04x\n", 29562306a36Sopenharmony_ci state->tx_abort_source, state->op->target); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci state->ret = -EIO; 30062306a36Sopenharmony_ci goto out; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Check for data in the Rx fifo */ 30462306a36Sopenharmony_ci xgbe_i2c_read(pdata); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* Fill up the Tx fifo next */ 30762306a36Sopenharmony_ci xgbe_i2c_write(pdata); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ciout: 31062306a36Sopenharmony_ci /* Complete on an error or STOP condition */ 31162306a36Sopenharmony_ci if (state->ret || XI2C_GET_BITS(isr, IC_RAW_INTR_STAT, STOP_DET)) 31262306a36Sopenharmony_ci complete(&pdata->i2c_complete); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_cireissue_check: 31562306a36Sopenharmony_ci /* Reissue interrupt if status is not clear */ 31662306a36Sopenharmony_ci if (pdata->vdata->irq_reissue_support) 31762306a36Sopenharmony_ci XP_IOWRITE(pdata, XP_INT_REISSUE_EN, 1 << 2); 31862306a36Sopenharmony_ci} 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_cistatic irqreturn_t xgbe_i2c_isr(int irq, void *data) 32162306a36Sopenharmony_ci{ 32262306a36Sopenharmony_ci struct xgbe_prv_data *pdata = (struct xgbe_prv_data *)data; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (pdata->isr_as_tasklet) 32562306a36Sopenharmony_ci tasklet_schedule(&pdata->tasklet_i2c); 32662306a36Sopenharmony_ci else 32762306a36Sopenharmony_ci xgbe_i2c_isr_task(&pdata->tasklet_i2c); 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return IRQ_HANDLED; 33062306a36Sopenharmony_ci} 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_cistatic void xgbe_i2c_set_mode(struct xgbe_prv_data *pdata) 33362306a36Sopenharmony_ci{ 33462306a36Sopenharmony_ci unsigned int reg; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci reg = XI2C_IOREAD(pdata, IC_CON); 33762306a36Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, MASTER_MODE, 1); 33862306a36Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, SLAVE_DISABLE, 1); 33962306a36Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, RESTART_EN, 1); 34062306a36Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, SPEED, XGBE_STD_SPEED); 34162306a36Sopenharmony_ci XI2C_SET_BITS(reg, IC_CON, RX_FIFO_FULL_HOLD, 1); 34262306a36Sopenharmony_ci XI2C_IOWRITE(pdata, IC_CON, reg); 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_cistatic void xgbe_i2c_get_features(struct xgbe_prv_data *pdata) 34662306a36Sopenharmony_ci{ 34762306a36Sopenharmony_ci struct xgbe_i2c *i2c = &pdata->i2c; 34862306a36Sopenharmony_ci unsigned int reg; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci reg = XI2C_IOREAD(pdata, IC_COMP_PARAM_1); 35162306a36Sopenharmony_ci i2c->max_speed_mode = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, 35262306a36Sopenharmony_ci MAX_SPEED_MODE); 35362306a36Sopenharmony_ci i2c->rx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, 35462306a36Sopenharmony_ci RX_BUFFER_DEPTH); 35562306a36Sopenharmony_ci i2c->tx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1, 35662306a36Sopenharmony_ci TX_BUFFER_DEPTH); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci if (netif_msg_probe(pdata)) 35962306a36Sopenharmony_ci dev_dbg(pdata->dev, "I2C features: %s=%u, %s=%u, %s=%u\n", 36062306a36Sopenharmony_ci "MAX_SPEED_MODE", i2c->max_speed_mode, 36162306a36Sopenharmony_ci "RX_BUFFER_DEPTH", i2c->rx_fifo_size, 36262306a36Sopenharmony_ci "TX_BUFFER_DEPTH", i2c->tx_fifo_size); 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_cistatic void xgbe_i2c_set_target(struct xgbe_prv_data *pdata, unsigned int addr) 36662306a36Sopenharmony_ci{ 36762306a36Sopenharmony_ci XI2C_IOWRITE(pdata, IC_TAR, addr); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic irqreturn_t xgbe_i2c_combined_isr(struct xgbe_prv_data *pdata) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci xgbe_i2c_isr_task(&pdata->tasklet_i2c); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci return IRQ_HANDLED; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic int xgbe_i2c_xfer(struct xgbe_prv_data *pdata, struct xgbe_i2c_op *op) 37862306a36Sopenharmony_ci{ 37962306a36Sopenharmony_ci struct xgbe_i2c_op_state *state = &pdata->i2c.op_state; 38062306a36Sopenharmony_ci int ret; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci mutex_lock(&pdata->i2c_mutex); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci reinit_completion(&pdata->i2c_complete); 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci ret = xgbe_i2c_disable(pdata); 38762306a36Sopenharmony_ci if (ret) { 38862306a36Sopenharmony_ci netdev_err(pdata->netdev, "failed to disable i2c master\n"); 38962306a36Sopenharmony_ci goto unlock; 39062306a36Sopenharmony_ci } 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci xgbe_i2c_set_target(pdata, op->target); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci memset(state, 0, sizeof(*state)); 39562306a36Sopenharmony_ci state->op = op; 39662306a36Sopenharmony_ci state->tx_len = op->len; 39762306a36Sopenharmony_ci state->tx_buf = op->buf; 39862306a36Sopenharmony_ci state->rx_len = op->len; 39962306a36Sopenharmony_ci state->rx_buf = op->buf; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci xgbe_i2c_clear_all_interrupts(pdata); 40262306a36Sopenharmony_ci ret = xgbe_i2c_enable(pdata); 40362306a36Sopenharmony_ci if (ret) { 40462306a36Sopenharmony_ci netdev_err(pdata->netdev, "failed to enable i2c master\n"); 40562306a36Sopenharmony_ci goto unlock; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Enabling the interrupts will cause the TX FIFO empty interrupt to 40962306a36Sopenharmony_ci * fire and begin to process the command via the ISR. 41062306a36Sopenharmony_ci */ 41162306a36Sopenharmony_ci xgbe_i2c_enable_interrupts(pdata); 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci if (!wait_for_completion_timeout(&pdata->i2c_complete, HZ)) { 41462306a36Sopenharmony_ci netdev_err(pdata->netdev, "i2c operation timed out\n"); 41562306a36Sopenharmony_ci ret = -ETIMEDOUT; 41662306a36Sopenharmony_ci goto disable; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci ret = state->ret; 42062306a36Sopenharmony_ci if (ret) { 42162306a36Sopenharmony_ci if (state->tx_abort_source & IC_TX_ABRT_7B_ADDR_NOACK) 42262306a36Sopenharmony_ci ret = -ENOTCONN; 42362306a36Sopenharmony_ci else if (state->tx_abort_source & IC_TX_ABRT_ARB_LOST) 42462306a36Sopenharmony_ci ret = -EAGAIN; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cidisable: 42862306a36Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 42962306a36Sopenharmony_ci xgbe_i2c_disable(pdata); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciunlock: 43262306a36Sopenharmony_ci mutex_unlock(&pdata->i2c_mutex); 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci return ret; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic void xgbe_i2c_stop(struct xgbe_prv_data *pdata) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci if (!pdata->i2c.started) 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci netif_dbg(pdata, link, pdata->netdev, "stopping I2C\n"); 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci pdata->i2c.started = 0; 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 44762306a36Sopenharmony_ci xgbe_i2c_disable(pdata); 44862306a36Sopenharmony_ci xgbe_i2c_clear_all_interrupts(pdata); 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (pdata->dev_irq != pdata->i2c_irq) { 45162306a36Sopenharmony_ci devm_free_irq(pdata->dev, pdata->i2c_irq, pdata); 45262306a36Sopenharmony_ci tasklet_kill(&pdata->tasklet_i2c); 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci} 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_cistatic int xgbe_i2c_start(struct xgbe_prv_data *pdata) 45762306a36Sopenharmony_ci{ 45862306a36Sopenharmony_ci int ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci if (pdata->i2c.started) 46162306a36Sopenharmony_ci return 0; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci netif_dbg(pdata, link, pdata->netdev, "starting I2C\n"); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci /* If we have a separate I2C irq, enable it */ 46662306a36Sopenharmony_ci if (pdata->dev_irq != pdata->i2c_irq) { 46762306a36Sopenharmony_ci tasklet_setup(&pdata->tasklet_i2c, xgbe_i2c_isr_task); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci ret = devm_request_irq(pdata->dev, pdata->i2c_irq, 47062306a36Sopenharmony_ci xgbe_i2c_isr, 0, pdata->i2c_name, 47162306a36Sopenharmony_ci pdata); 47262306a36Sopenharmony_ci if (ret) { 47362306a36Sopenharmony_ci netdev_err(pdata->netdev, "i2c irq request failed\n"); 47462306a36Sopenharmony_ci return ret; 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci } 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci pdata->i2c.started = 1; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_cistatic int xgbe_i2c_init(struct xgbe_prv_data *pdata) 48462306a36Sopenharmony_ci{ 48562306a36Sopenharmony_ci int ret; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci xgbe_i2c_disable_interrupts(pdata); 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci ret = xgbe_i2c_disable(pdata); 49062306a36Sopenharmony_ci if (ret) { 49162306a36Sopenharmony_ci dev_err(pdata->dev, "failed to disable i2c master\n"); 49262306a36Sopenharmony_ci return ret; 49362306a36Sopenharmony_ci } 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci xgbe_i2c_get_features(pdata); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci xgbe_i2c_set_mode(pdata); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci xgbe_i2c_clear_all_interrupts(pdata); 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci return 0; 50262306a36Sopenharmony_ci} 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_civoid xgbe_init_function_ptrs_i2c(struct xgbe_i2c_if *i2c_if) 50562306a36Sopenharmony_ci{ 50662306a36Sopenharmony_ci i2c_if->i2c_init = xgbe_i2c_init; 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci i2c_if->i2c_start = xgbe_i2c_start; 50962306a36Sopenharmony_ci i2c_if->i2c_stop = xgbe_i2c_stop; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci i2c_if->i2c_xfer = xgbe_i2c_xfer; 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci i2c_if->i2c_isr = xgbe_i2c_combined_isr; 51462306a36Sopenharmony_ci} 515