diff options
Diffstat (limited to 'drivers/net/ethernet/xilinx/xilinx_tsn_preemption.c')
-rw-r--r-- | drivers/net/ethernet/xilinx/xilinx_tsn_preemption.c | 223 |
1 files changed, 223 insertions, 0 deletions
diff --git a/drivers/net/ethernet/xilinx/xilinx_tsn_preemption.c b/drivers/net/ethernet/xilinx/xilinx_tsn_preemption.c new file mode 100644 index 000000000000..f48c2e0cb69e --- /dev/null +++ b/drivers/net/ethernet/xilinx/xilinx_tsn_preemption.c @@ -0,0 +1,223 @@ +/* + * Xilinx FPGA Xilinx TSN QBU/QBR - Frame Preemption module. + * + * Copyright (c) 2017 Xilinx Pvt., Ltd + * + * Author: Priyadarshini Babu <priyadar@xilinx.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "xilinx_axienet.h" +#include "xilinx_tsn_preemption.h" + +/** + * axienet_preemption - Configure Frame Preemption + * @ndev: Pointer to the net_device structure + * @useraddr: Value to be programmed + * Return: 0 on success, Non-zero error value on failure + */ +int axienet_preemption(struct net_device *ndev, void __user *useraddr) +{ + struct axienet_local *lp = netdev_priv(ndev); + u8 preemp; + + if (copy_from_user(&preemp, useraddr, sizeof(preemp))) + return -EFAULT; + + axienet_iow(lp, PREEMPTION_ENABLE_REG, preemp & PREEMPTION_ENABLE); + return 0; +} + +/** + * axienet_preemption_ctrl - Configure Frame Preemption Control register + * @ndev: Pointer to the net_device structure + * @useraddr: Value to be programmed + * Return: 0 on success, Non-zero error value on failure + */ +int axienet_preemption_ctrl(struct net_device *ndev, void __user *useraddr) +{ + struct axienet_local *lp = netdev_priv(ndev); + struct preempt_ctrl_sts data; + u32 value; + + if (copy_from_user(&data, useraddr, sizeof(struct preempt_ctrl_sts))) + return -EFAULT; + value = axienet_ior(lp, PREEMPTION_CTRL_STS_REG); + + value &= ~(VERIFY_TIMER_VALUE_MASK << VERIFY_TIMER_VALUE_SHIFT); + value |= (data.verify_timer_value << VERIFY_TIMER_VALUE_SHIFT); + value &= ~(ADDITIONAL_FRAG_SIZE_MASK << ADDITIONAL_FRAG_SIZE_SHIFT); + value |= (data.additional_frag_size << ADDITIONAL_FRAG_SIZE_SHIFT); + value &= ~(DISABLE_PREEMPTION_VERIFY); + value |= (data.disable_preemp_verify); + + axienet_iow(lp, PREEMPTION_CTRL_STS_REG, value); + return 0; +} + +/** + * axienet_preemption_sts - Get Frame Preemption Status + * @ndev: Pointer to the net_device structure + * @useraddr: return value, containing Frame Preemption status + * Return: 0 on success, Non-zero error value on failure + */ +int axienet_preemption_sts(struct net_device *ndev, void __user *useraddr) +{ + struct axienet_local *lp = netdev_priv(ndev); + struct preempt_ctrl_sts status; + u32 value; + + value = axienet_ior(lp, PREEMPTION_CTRL_STS_REG); + + status.tx_preemp_sts = (value & TX_PREEMPTION_STS) ? 1 : 0; + status.mac_tx_verify_sts = (value >> MAC_MERGE_TX_VERIFY_STS_SHIFT) & + MAC_MERGE_TX_VERIFY_STS_MASK; + status.verify_timer_value = (value >> VERIFY_TIMER_VALUE_SHIFT) & + VERIFY_TIMER_VALUE_MASK; + status.additional_frag_size = (value >> ADDITIONAL_FRAG_SIZE_SHIFT) & + ADDITIONAL_FRAG_SIZE_MASK; + status.disable_preemp_verify = value & DISABLE_PREEMPTION_VERIFY; + + if (copy_to_user(useraddr, &status, sizeof(struct preempt_ctrl_sts))) + return -EFAULT; + return 0; +} + +/** + * statistic_cnts - Read statistics counter registers + * @ndev: Pointer to the net_device structure + * @ptr: Buffer addr to fill the counter values + * @count: read #count number of registers + * @addr_off: Register address to be read + */ +static void statistic_cnts(struct net_device *ndev, void *ptr, + unsigned int count, unsigned int addr_off) +{ + struct axienet_local *lp = netdev_priv(ndev); + int *buf = (int *)ptr; + int i = 0; + + for (i = 0; i < count; i++) { + buf[i] = axienet_ior(lp, addr_off); + addr_off += 4; + } +} + +/** + * axienet_preemption_cnt - Get Frame Preemption Statistics counter + * @ndev: Pointer to the net_device structure + * @useraddr: return value, containing counters value + * Return: 0 on success, Non-zero error value on failure + */ +int axienet_preemption_cnt(struct net_device *ndev, void __user *useraddr) +{ + struct axienet_local *lp = netdev_priv(ndev); + struct emac_pmac_stats stats; + + statistic_cnts(ndev, &stats.emac, + sizeof(struct statistics_counters) / 4, + RX_BYTES_EMAC_REG); + + stats.preemp_en = axienet_ior(lp, PREEMPTION_ENABLE_REG); + if (stats.preemp_en) { + statistic_cnts(ndev, &stats.pmac.sts, + sizeof(struct statistics_counters) / 4, + RX_BYTES_PMAC_REG); + statistic_cnts(ndev, &stats.pmac.merge, + sizeof(struct mac_merge_counters) / 4, + TX_HOLD_REG); + } + + if (copy_to_user(useraddr, &stats, sizeof(struct emac_pmac_stats))) + return -EFAULT; + return 0; +} + +/** + * axienet_qbu_user_override - Configure QBU user override register + * @ndev: Pointer to the net_device structure + * @useraddr: Value to be programmed + * Return: 0 on success, Non-zero error value on failure + */ +int axienet_qbu_user_override(struct net_device *ndev, void __user *useraddr) +{ + struct axienet_local *lp = netdev_priv(ndev); + struct qbu_user data; + u32 value; + + if (copy_from_user(&data, useraddr, sizeof(struct qbu_user))) + return -EFAULT; + + value = axienet_ior(lp, QBU_USER_OVERRIDE_REG); + + if (data.set & QBU_WINDOW) { + if (data.user.hold_rel_window) { + value |= USER_HOLD_REL_ENABLE_VALUE; + value |= HOLD_REL_WINDOW_OVERRIDE; + } else { + value &= ~(USER_HOLD_REL_ENABLE_VALUE); + value &= ~(HOLD_REL_WINDOW_OVERRIDE); + } + } + if (data.set & QBU_GUARD_BAND) { + if (data.user.guard_band) + value |= GUARD_BAND_OVERRUN_CNT_INC_OVERRIDE; + else + value &= ~(GUARD_BAND_OVERRUN_CNT_INC_OVERRIDE); + } + if (data.set & QBU_HOLD_TIME) { + if (data.user.hold_time_override) { + value |= HOLD_TIME_OVERRIDE; + value &= ~(USER_HOLD_TIME_MASK << USER_HOLD_TIME_SHIFT); + value |= data.user.user_hold_time << + USER_HOLD_TIME_SHIFT; + } else { + value &= ~(HOLD_TIME_OVERRIDE); + value &= ~(USER_HOLD_TIME_MASK << USER_HOLD_TIME_SHIFT); + } + } + if (data.set & QBU_REL_TIME) { + if (data.user.rel_time_override) { + value |= REL_TIME_OVERRIDE; + value &= ~(USER_REL_TIME_MASK << USER_REL_TIME_SHIFT); + value |= data.user.user_rel_time << USER_REL_TIME_SHIFT; + } else { + value &= ~(REL_TIME_OVERRIDE); + value &= ~(USER_REL_TIME_MASK << USER_REL_TIME_SHIFT); + } + } + + axienet_iow(lp, QBU_USER_OVERRIDE_REG, value); + return 0; +} + +/** + * axienet_qbu_sts - Get QBU Core status + * @ndev: Pointer to the net_device structure + * @useraddr: return value, containing QBU core status value + * Return: 0 on success, Non-zero error value on failure + */ +int axienet_qbu_sts(struct net_device *ndev, void __user *useraddr) +{ + struct axienet_local *lp = netdev_priv(ndev); + struct qbu_core_status status; + u32 value = 0; + + value = axienet_ior(lp, QBU_CORE_STS_REG); + status.hold_time = (value >> HOLD_TIME_STS_SHIFT) & HOLD_TIME_STS_MASK; + status.rel_time = (value >> REL_TIME_STS_SHIFT) & REL_TIME_STS_MASK; + status.hold_rel_en = (value & HOLD_REL_ENABLE_STS) ? 1 : 0; + status.pmac_hold_req = value & PMAC_HOLD_REQ_STS; + + if (copy_to_user(useraddr, &status, sizeof(struct qbu_core_status))) + return -EFAULT; + return 0; +} |