From f5ed3aa62075849b1687731349e5b679c732118a Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 27 Oct 2017 11:50:01 +0200 Subject: sdk_dpaa: Import from QorIQ SDK Linux http://git.freescale.com/git/cgit.cgi/ppc/sdk/linux.git Commit 1ae843c08261402b2c35d83422e4fa1e313611f4 (fsl-sdk-v2.0-1703). Update #3277. --- .../ethernet/freescale/sdk_dpaa/dpaa_eth_base.c | 265 +++++++++++ .../ethernet/freescale/sdk_dpaa/dpaa_eth_base.h | 50 +++ .../ethernet/freescale/sdk_dpaa/dpaa_eth_common.h | 229 ++++++++++ .../ethernet/freescale/sdk_dpaa/dpaa_eth_macless.c | 498 +++++++++++++++++++++ 4 files changed, 1042 insertions(+) create mode 100644 linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.c create mode 100644 linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.h create mode 100644 linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.h create mode 100644 linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_macless.c (limited to 'linux') diff --git a/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.c b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.c new file mode 100644 index 00000000..2256d4f1 --- /dev/null +++ b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.c @@ -0,0 +1,265 @@ +#include + +/* Copyright 2008-2013 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef CONFIG_FSL_DPAA_ETH_DEBUG +#define pr_fmt(fmt) \ + KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ + KBUILD_BASENAME".c", __LINE__, __func__ +#else +#define pr_fmt(fmt) \ + KBUILD_MODNAME ": " fmt +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dpaa_eth.h" +#include "dpaa_eth_common.h" +#include "dpaa_eth_base.h" + +#define DPA_DESCRIPTION "FSL DPAA Advanced drivers:" + +MODULE_LICENSE("Dual BSD/GPL"); + +uint8_t advanced_debug = -1; +module_param(advanced_debug, byte, S_IRUGO); +MODULE_PARM_DESC(advanced_debug, "Module/Driver verbosity level"); +EXPORT_SYMBOL(advanced_debug); + +static int dpa_bp_cmp(const void *dpa_bp0, const void *dpa_bp1) +{ + return ((struct dpa_bp *)dpa_bp0)->size - + ((struct dpa_bp *)dpa_bp1)->size; +} + +struct dpa_bp * __cold __must_check /* __attribute__((nonnull)) */ +dpa_bp_probe(struct platform_device *_of_dev, size_t *count) +{ + int i, lenp, na, ns, err; + struct device *dev; + struct device_node *dev_node; + const __be32 *bpool_cfg; + struct dpa_bp *dpa_bp; + u32 bpid; + + dev = &_of_dev->dev; + + *count = of_count_phandle_with_args(dev->of_node, + "fsl,bman-buffer-pools", NULL); + if (*count < 1) { + dev_err(dev, "missing fsl,bman-buffer-pools device tree entry\n"); + return ERR_PTR(-EINVAL); + } + + dpa_bp = devm_kzalloc(dev, *count * sizeof(*dpa_bp), GFP_KERNEL); + if (dpa_bp == NULL) { + dev_err(dev, "devm_kzalloc() failed\n"); + return ERR_PTR(-ENOMEM); + } + + dev_node = of_find_node_by_path("/"); + if (unlikely(dev_node == NULL)) { + dev_err(dev, "of_find_node_by_path(/) failed\n"); + return ERR_PTR(-EINVAL); + } + + na = of_n_addr_cells(dev_node); + ns = of_n_size_cells(dev_node); + + for (i = 0; i < *count; i++) { + of_node_put(dev_node); + + dev_node = of_parse_phandle(dev->of_node, + "fsl,bman-buffer-pools", i); + if (dev_node == NULL) { + dev_err(dev, "of_find_node_by_phandle() failed\n"); + return ERR_PTR(-EFAULT); + } + + if (unlikely(!of_device_is_compatible(dev_node, "fsl,bpool"))) { + dev_err(dev, + "!of_device_is_compatible(%s, fsl,bpool)\n", + dev_node->full_name); + dpa_bp = ERR_PTR(-EINVAL); + goto _return_of_node_put; + } + + err = of_property_read_u32(dev_node, "fsl,bpid", &bpid); + if (err) { + dev_err(dev, "Cannot find buffer pool ID in the device tree\n"); + dpa_bp = ERR_PTR(-EINVAL); + goto _return_of_node_put; + } + dpa_bp[i].bpid = (uint8_t)bpid; + + bpool_cfg = of_get_property(dev_node, "fsl,bpool-ethernet-cfg", + &lenp); + if (bpool_cfg && (lenp == (2 * ns + na) * sizeof(*bpool_cfg))) { + const uint32_t *seed_pool; + + dpa_bp[i].config_count = + (int)of_read_number(bpool_cfg, ns); + dpa_bp[i].size = + (size_t)of_read_number(bpool_cfg + ns, ns); + dpa_bp[i].paddr = + of_read_number(bpool_cfg + 2 * ns, na); + + seed_pool = of_get_property(dev_node, + "fsl,bpool-ethernet-seeds", &lenp); + dpa_bp[i].seed_pool = !!seed_pool; + + } else { + dev_err(dev, + "Missing/invalid fsl,bpool-ethernet-cfg device tree entry for node %s\n", + dev_node->full_name); + dpa_bp = ERR_PTR(-EINVAL); + goto _return_of_node_put; + } + } + + sort(dpa_bp, *count, sizeof(*dpa_bp), dpa_bp_cmp, NULL); + + return dpa_bp; + +_return_of_node_put: + if (dev_node) + of_node_put(dev_node); + + return dpa_bp; +} +EXPORT_SYMBOL(dpa_bp_probe); + +int dpa_bp_shared_port_seed(struct dpa_bp *bp) +{ + void __iomem **ptr; + + /* In MAC-less and Shared-MAC scenarios the physical + * address of the buffer pool in device tree is set + * to 0 to specify that another entity (USDPAA) will + * allocate and seed the buffers + */ + if (!bp->paddr) + return 0; + + /* allocate memory region for buffers */ + devm_request_mem_region(bp->dev, bp->paddr, + bp->size * bp->config_count, KBUILD_MODNAME); + /* managed ioremap unmapping */ + ptr = devres_alloc(devm_ioremap_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return -EIO; +#ifndef CONFIG_PPC + bp->vaddr = ioremap_cache_ns(bp->paddr, bp->size * bp->config_count); +#else + bp->vaddr = ioremap_prot(bp->paddr, bp->size * bp->config_count, 0); +#endif + if (bp->vaddr == NULL) { + pr_err("Could not map memory for pool %d\n", bp->bpid); + devres_free(ptr); + return -EIO; + } + *ptr = bp->vaddr; + devres_add(bp->dev, ptr); + + /* seed pool with buffers from that memory region */ + if (bp->seed_pool) { + int count = bp->target_count; + dma_addr_t addr = bp->paddr; + + while (count) { + struct bm_buffer bufs[8]; + uint8_t num_bufs = 0; + + do { + BUG_ON(addr > 0xffffffffffffull); + bufs[num_bufs].bpid = bp->bpid; + bm_buffer_set64(&bufs[num_bufs++], addr); + addr += bp->size; + + } while (--count && (num_bufs < 8)); + + while (bman_release(bp->pool, bufs, num_bufs, 0)) + cpu_relax(); + } + } + + return 0; +} +EXPORT_SYMBOL(dpa_bp_shared_port_seed); + +int dpa_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, + size_t count) +{ + struct dpa_priv_s *priv = netdev_priv(net_dev); + int i; + + priv->dpa_bp = dpa_bp; + priv->bp_count = count; + + for (i = 0; i < count; i++) { + int err; + err = dpa_bp_alloc(&dpa_bp[i]); + if (err < 0) { + dpa_bp_free(priv); + priv->dpa_bp = NULL; + return err; + } + } + + return 0; +} +EXPORT_SYMBOL(dpa_bp_create); + +static int __init __cold dpa_advanced_load(void) +{ + pr_info(DPA_DESCRIPTION "\n"); + + return 0; +} +module_init(dpa_advanced_load); + +static void __exit __cold dpa_advanced_unload(void) +{ + pr_debug(KBUILD_MODNAME ": -> %s:%s()\n", + KBUILD_BASENAME".c", __func__); + +} +module_exit(dpa_advanced_unload); diff --git a/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.h b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.h new file mode 100644 index 00000000..6f570413 --- /dev/null +++ b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_base.h @@ -0,0 +1,50 @@ +/* Copyright 2008-2013 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DPAA_ETH_BASE_H +#define __DPAA_ETH_BASE_H + +#include /* struct net_device */ +#include /* struct bm_buffer */ +#include /* struct platform_device */ +#include /* struct hwtstamp_config */ + +extern uint8_t advanced_debug; +extern const struct dpa_fq_cbs_t shared_fq_cbs; +extern int __hot dpa_shared_tx(struct sk_buff *skb, struct net_device *net_dev); + +struct dpa_bp * __cold __must_check /* __attribute__((nonnull)) */ +dpa_bp_probe(struct platform_device *_of_dev, size_t *count); +int dpa_bp_create(struct net_device *net_dev, struct dpa_bp *dpa_bp, + size_t count); +int dpa_bp_shared_port_seed(struct dpa_bp *bp); + +#endif /* __DPAA_ETH_BASE_H */ diff --git a/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.h b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.h new file mode 100644 index 00000000..022b4c2d --- /dev/null +++ b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_common.h @@ -0,0 +1,229 @@ +/* Copyright 2008-2013 Freescale Semiconductor, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __DPAA_ETH_COMMON_H +#define __DPAA_ETH_COMMON_H + +#include /* struct net_device */ +#include /* struct bm_buffer */ +#include /* struct platform_device */ +#include /* struct hwtstamp_config */ + +#include "dpaa_eth.h" +#include "lnxwrp_fsl_fman.h" + +#define dpaa_eth_init_port(type, port, param, errq_id, defq_id, buf_layout,\ + frag_enabled) \ +{ \ + param.errq = errq_id; \ + param.defq = defq_id; \ + param.priv_data_size = buf_layout->priv_data_size; \ + param.parse_results = buf_layout->parse_results; \ + param.hash_results = buf_layout->hash_results; \ + param.frag_enable = frag_enabled; \ + param.time_stamp = buf_layout->time_stamp; \ + param.manip_extra_space = buf_layout->manip_extra_space; \ + param.data_align = buf_layout->data_align; \ + fm_set_##type##_port_params(port, ¶m); \ +} + +#define DPA_SGT_MAX_ENTRIES 16 /* maximum number of entries in SG Table */ + +#define DPA_SGT_ENTRIES_THRESHOLD DPA_SGT_MAX_ENTRIES +#ifndef CONFIG_PPC +/* each S/G entry can be divided into two S/G entries during errata W/A */ +#define DPA_SGT_4K_ENTRIES_THRESHOLD 7 +#endif + +#define DPA_BUFF_RELEASE_MAX 8 /* maximum number of buffers released at once */ + +#define DPA_RX_PCD_HI_PRIO_FQ_INIT_FAIL(dpa_fq, _errno) \ + (((dpa_fq)->fq_type == FQ_TYPE_RX_PCD_HI_PRIO) && \ + (_errno == -EIO)) +/* return codes for the dpaa-eth hooks */ +enum dpaa_eth_hook_result { + /* fd/skb was retained by the hook. + * + * On the Rx path, this means the Ethernet driver will _not_ + * deliver the skb to the stack. Instead, the hook implementation + * is expected to properly dispose of the skb. + * + * On the Tx path, the Ethernet driver's dpa_tx() function will + * immediately return NETDEV_TX_OK. The hook implementation is expected + * to free the skb. *DO*NOT* release it to BMan, or enqueue it to FMan, + * unless you know exactly what you're doing! + * + * On the confirmation/error paths, the Ethernet driver will _not_ + * perform any fd cleanup, nor update the interface statistics. + */ + DPAA_ETH_STOLEN, + /* fd/skb was returned to the Ethernet driver for regular processing. + * The hook is not allowed to, for instance, reallocate the skb (as if + * by linearizing, copying, cloning or reallocating the headroom). + */ + DPAA_ETH_CONTINUE +}; + +typedef enum dpaa_eth_hook_result (*dpaa_eth_ingress_hook_t)( + struct sk_buff *skb, struct net_device *net_dev, u32 fqid); +typedef enum dpaa_eth_hook_result (*dpaa_eth_egress_hook_t)( + struct sk_buff *skb, struct net_device *net_dev); +typedef enum dpaa_eth_hook_result (*dpaa_eth_confirm_hook_t)( + struct net_device *net_dev, const struct qm_fd *fd, u32 fqid); + +/* used in napi related functions */ +extern u16 qman_portal_max; + +/* from dpa_ethtool.c */ +extern const struct ethtool_ops dpa_ethtool_ops; + +#ifdef CONFIG_FSL_DPAA_HOOKS +/* Various hooks used for unit-testing and/or fastpath optimizations. + * Currently only one set of such hooks is supported. + */ +struct dpaa_eth_hooks_s { + /* Invoked on the Tx private path, immediately after receiving the skb + * from the stack. + */ + dpaa_eth_egress_hook_t tx; + + /* Invoked on the Rx private path, right before passing the skb + * up the stack. At that point, the packet's protocol id has already + * been set. The skb's data pointer is now at the L3 header, and + * skb->mac_header points to the L2 header. skb->len has been adjusted + * to be the length of L3+payload (i.e., the length of the + * original frame minus the L2 header len). + * For more details on what the skb looks like, see eth_type_trans(). + */ + dpaa_eth_ingress_hook_t rx_default; + + /* Driver hook for the Rx error private path. */ + dpaa_eth_confirm_hook_t rx_error; + /* Driver hook for the Tx confirmation private path. */ + dpaa_eth_confirm_hook_t tx_confirm; + /* Driver hook for the Tx error private path. */ + dpaa_eth_confirm_hook_t tx_error; +}; + +void fsl_dpaa_eth_set_hooks(struct dpaa_eth_hooks_s *hooks); + +extern struct dpaa_eth_hooks_s dpaa_eth_hooks; +#endif + +int dpa_netdev_init(struct net_device *net_dev, + const uint8_t *mac_addr, + uint16_t tx_timeout); +int __cold dpa_start(struct net_device *net_dev); +int __cold dpa_stop(struct net_device *net_dev); +void __cold dpa_timeout(struct net_device *net_dev); +struct rtnl_link_stats64 * __cold +dpa_get_stats64(struct net_device *net_dev, + struct rtnl_link_stats64 *stats); +int dpa_change_mtu(struct net_device *net_dev, int new_mtu); +int dpa_ndo_init(struct net_device *net_dev); +int dpa_set_features(struct net_device *dev, netdev_features_t features); +netdev_features_t dpa_fix_features(struct net_device *dev, + netdev_features_t features); +#ifdef CONFIG_FSL_DPAA_TS +u64 dpa_get_timestamp_ns(const struct dpa_priv_s *priv, + enum port_type rx_tx, const void *data); +/* Updates the skb shared hw timestamp from the hardware timestamp */ +int dpa_get_ts(const struct dpa_priv_s *priv, enum port_type rx_tx, + struct skb_shared_hwtstamps *shhwtstamps, const void *data); +#endif /* CONFIG_FSL_DPAA_TS */ +int dpa_ioctl(struct net_device *dev, struct ifreq *rq, int cmd); +int __cold dpa_remove(struct platform_device *of_dev); +struct mac_device * __cold __must_check +__attribute__((nonnull)) dpa_mac_probe(struct platform_device *_of_dev); +int dpa_set_mac_address(struct net_device *net_dev, void *addr); +void dpa_set_rx_mode(struct net_device *net_dev); +void dpa_set_buffers_layout(struct mac_device *mac_dev, + struct dpa_buffer_layout_s *layout); +int __attribute__((nonnull)) +dpa_bp_alloc(struct dpa_bp *dpa_bp); +void __cold __attribute__((nonnull)) +dpa_bp_free(struct dpa_priv_s *priv); +struct dpa_bp *dpa_bpid2pool(int bpid); +void dpa_bpid2pool_map(int bpid, struct dpa_bp *dpa_bp); +bool dpa_bpid2pool_use(int bpid); +void dpa_bp_drain(struct dpa_bp *bp); +#ifdef CONFIG_FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE +u16 dpa_select_queue(struct net_device *net_dev, struct sk_buff *skb, + void *accel_priv, select_queue_fallback_t fallback); +#endif +struct dpa_fq *dpa_fq_alloc(struct device *dev, + u32 fq_start, + u32 fq_count, + struct list_head *list, + enum dpa_fq_type fq_type); +int dpa_fq_probe_mac(struct device *dev, struct list_head *list, + struct fm_port_fqs *port_fqs, + bool tx_conf_fqs_per_core, + enum port_type ptype); +int dpa_get_channel(void); +void dpa_release_channel(void); +int dpaa_eth_add_channel(void *__arg); +int dpaa_eth_cgr_init(struct dpa_priv_s *priv); +void dpa_fq_setup(struct dpa_priv_s *priv, const struct dpa_fq_cbs_t *fq_cbs, + struct fm_port *tx_port); +int dpa_fq_init(struct dpa_fq *dpa_fq, bool td_enable); +int dpa_fqs_init(struct device *dev, struct list_head *list, bool td_enable); +int __cold __attribute__((nonnull)) +dpa_fq_free(struct device *dev, struct list_head *list); +void dpaa_eth_init_ports(struct mac_device *mac_dev, + struct dpa_bp *bp, size_t count, + struct fm_port_fqs *port_fqs, + struct dpa_buffer_layout_s *buf_layout, + struct device *dev); +void dpa_release_sgt(struct qm_sg_entry *sgt); +void __attribute__((nonnull)) +dpa_fd_release(const struct net_device *net_dev, const struct qm_fd *fd); +void count_ern(struct dpa_percpu_priv_s *percpu_priv, + const struct qm_mr_entry *msg); +int dpa_enable_tx_csum(struct dpa_priv_s *priv, + struct sk_buff *skb, struct qm_fd *fd, char *parse_results); +#ifdef CONFIG_FSL_DPAA_CEETM +void dpa_enable_ceetm(struct net_device *dev); +void dpa_disable_ceetm(struct net_device *dev); +#endif +struct proxy_device { + struct mac_device *mac_dev; +}; + +/* mac device control functions exposed by proxy interface*/ +int dpa_proxy_start(struct net_device *net_dev); +int dpa_proxy_stop(struct proxy_device *proxy_dev, struct net_device *net_dev); +int dpa_proxy_set_mac_address(struct proxy_device *proxy_dev, + struct net_device *net_dev); +int dpa_proxy_set_rx_mode(struct proxy_device *proxy_dev, + struct net_device *net_dev); + +#endif /* __DPAA_ETH_COMMON_H */ diff --git a/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_macless.c b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_macless.c new file mode 100644 index 00000000..b21afa30 --- /dev/null +++ b/linux/drivers/net/ethernet/freescale/sdk_dpaa/dpaa_eth_macless.c @@ -0,0 +1,498 @@ +#include + +/* Copyright 2008-2013 Freescale Semiconductor Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Freescale Semiconductor nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef CONFIG_FSL_DPAA_ETH_DEBUG +#define pr_fmt(fmt) \ + KBUILD_MODNAME ": %s:%hu:%s() " fmt, \ + KBUILD_BASENAME".c", __LINE__, __func__ +#else +#define pr_fmt(fmt) \ + KBUILD_MODNAME ": " fmt +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dpaa_eth.h" +#include "dpaa_eth_common.h" +#include "dpaa_eth_base.h" +#include "lnxwrp_fsl_fman.h" /* fm_get_rx_extra_headroom(), fm_get_max_frm() */ +#include "mac.h" + +/* For MAC-based interfaces, we compute the tx needed headroom from the + * associated Tx port's buffer layout settings. + * For MACless interfaces just use a default value. + */ +#define DPA_DEFAULT_TX_HEADROOM 64 + +#define DPA_DESCRIPTION "FSL DPAA MACless Ethernet driver" + +MODULE_LICENSE("Dual BSD/GPL"); + +MODULE_DESCRIPTION(DPA_DESCRIPTION); + +/* This has to work in tandem with the DPA_CS_THRESHOLD_xxx values. */ +static uint16_t macless_tx_timeout = 1000; +module_param(macless_tx_timeout, ushort, S_IRUGO); +MODULE_PARM_DESC(macless_tx_timeout, "The MACless Tx timeout in ms"); + +/* forward declarations */ +static int __cold dpa_macless_start(struct net_device *net_dev); +static int __cold dpa_macless_stop(struct net_device *net_dev); +static int __cold dpa_macless_set_address(struct net_device *net_dev, + void *addr); +static void __cold dpa_macless_set_rx_mode(struct net_device *net_dev); + +static int dpaa_eth_macless_probe(struct platform_device *_of_dev); +static netdev_features_t +dpa_macless_fix_features(struct net_device *dev, netdev_features_t features); + +static const struct net_device_ops dpa_macless_ops = { + .ndo_open = dpa_macless_start, + .ndo_start_xmit = dpa_shared_tx, + .ndo_stop = dpa_macless_stop, + .ndo_tx_timeout = dpa_timeout, + .ndo_get_stats64 = dpa_get_stats64, + .ndo_set_mac_address = dpa_macless_set_address, + .ndo_set_rx_mode = dpa_macless_set_rx_mode, + .ndo_validate_addr = eth_validate_addr, +#ifdef CONFIG_FSL_DPAA_ETH_USE_NDO_SELECT_QUEUE + .ndo_select_queue = dpa_select_queue, +#endif + .ndo_change_mtu = dpa_change_mtu, + .ndo_init = dpa_ndo_init, + .ndo_set_features = dpa_set_features, + .ndo_fix_features = dpa_macless_fix_features, +}; + +static const struct of_device_id dpa_macless_match[] = { + { + .compatible = "fsl,dpa-ethernet-macless" + }, + {} +}; +MODULE_DEVICE_TABLE(of, dpa_macless_match); + +static struct platform_driver dpa_macless_driver = { + .driver = { + .name = KBUILD_MODNAME "-macless", + .of_match_table = dpa_macless_match, + .owner = THIS_MODULE, + }, + .probe = dpaa_eth_macless_probe, + .remove = dpa_remove +}; + +static const char macless_frame_queues[][25] = { + [RX] = "fsl,qman-frame-queues-rx", + [TX] = "fsl,qman-frame-queues-tx" +}; + +static int __cold dpa_macless_start(struct net_device *net_dev) +{ + const struct dpa_priv_s *priv = netdev_priv(net_dev); + struct proxy_device *proxy_dev = (struct proxy_device *)priv->peer; + + netif_tx_start_all_queues(net_dev); + + if (proxy_dev) + dpa_proxy_start(net_dev); + + + return 0; +} + +static int __cold dpa_macless_stop(struct net_device *net_dev) +{ + const struct dpa_priv_s *priv = netdev_priv(net_dev); + struct proxy_device *proxy_dev = (struct proxy_device *)priv->peer; + + netif_tx_stop_all_queues(net_dev); + + if (proxy_dev) + dpa_proxy_stop(proxy_dev, net_dev); + + return 0; +} + +static int dpa_macless_set_address(struct net_device *net_dev, void *addr) +{ + const struct dpa_priv_s *priv = netdev_priv(net_dev); + struct proxy_device *proxy_dev = (struct proxy_device *)priv->peer; + int _errno; + + _errno = eth_mac_addr(net_dev, addr); + if (_errno < 0) { + if (netif_msg_drv(priv)) + netdev_err(net_dev, "eth_mac_addr() = %d\n", _errno); + return _errno; + } + + if (proxy_dev) { + _errno = dpa_proxy_set_mac_address(proxy_dev, net_dev); + if (_errno < 0) { + if (netif_msg_drv(priv)) + netdev_err(net_dev, "proxy_set_mac_address() = %d\n", + _errno); + return _errno; + } + } + + return 0; +} + +static void __cold dpa_macless_set_rx_mode(struct net_device *net_dev) +{ + const struct dpa_priv_s *priv = netdev_priv(net_dev); + struct proxy_device *proxy_dev = (struct proxy_device *)priv->peer; + + if (proxy_dev) + dpa_proxy_set_rx_mode(proxy_dev, net_dev); +} + +static netdev_features_t +dpa_macless_fix_features(struct net_device *dev, netdev_features_t features) +{ + netdev_features_t unsupported_features = 0; + + /* In theory we should never be requested to enable features that + * we didn't set in netdev->features and netdev->hw_features at probe + * time, but double check just to be on the safe side. + */ + unsupported_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; + /* We don't support enabling Rx csum through ethtool yet */ + unsupported_features |= NETIF_F_RXCSUM; + + features &= ~unsupported_features; + + return features; +} + +static int dpa_macless_netdev_init(struct device_node *dpa_node, + struct net_device *net_dev) +{ + struct dpa_priv_s *priv = netdev_priv(net_dev); + struct proxy_device *proxy_dev = (struct proxy_device *)priv->peer; + struct device *dev = net_dev->dev.parent; + const uint8_t *mac_addr; + + net_dev->netdev_ops = &dpa_macless_ops; + + if (proxy_dev) { + struct mac_device *mac_dev = proxy_dev->mac_dev; + net_dev->mem_start = mac_dev->res->start; + net_dev->mem_end = mac_dev->res->end; + + return dpa_netdev_init(net_dev, mac_dev->addr, + macless_tx_timeout); + } else { + /* Get the MAC address from device tree */ + mac_addr = of_get_mac_address(dpa_node); + + if (mac_addr == NULL) { + if (netif_msg_probe(priv)) + dev_err(dev, "No MAC address found!\n"); + return -EINVAL; + } + + return dpa_netdev_init(net_dev, mac_addr, + macless_tx_timeout); + } +} + +/* Probing of FQs for MACless ports */ +static int dpa_fq_probe_macless(struct device *dev, struct list_head *list, + enum port_type ptype) +{ + struct device_node *np = dev->of_node; + const struct fqid_cell *fqids; + int num_ranges; + int i, lenp; + + fqids = of_get_property(np, macless_frame_queues[ptype], &lenp); + if (fqids == NULL) { + dev_err(dev, "Need FQ definition in dts for MACless devices\n"); + return -EINVAL; + } + + num_ranges = lenp / sizeof(*fqids); + + /* All ranges defined in the device tree are used as Rx/Tx queues */ + for (i = 0; i < num_ranges; i++) { + if (!dpa_fq_alloc(dev, be32_to_cpu(fqids[i].start), + be32_to_cpu(fqids[i].count), list, + ptype == RX ? FQ_TYPE_RX_PCD : FQ_TYPE_TX)) { + dev_err(dev, "_dpa_fq_alloc() failed\n"); + return -ENOMEM; + } + } + + return 0; +} + + static struct proxy_device * +dpa_macless_proxy_probe(struct platform_device *_of_dev) +{ + struct device *dev; + const phandle *proxy_prop; + struct proxy_device *proxy_dev; + struct device_node *proxy_node; + struct platform_device *proxy_pdev; + int lenp; + + dev = &_of_dev->dev; + + proxy_prop = of_get_property(dev->of_node, "proxy", &lenp); + if (!proxy_prop) + return NULL; + + proxy_node = of_find_node_by_phandle(*proxy_prop); + if (!proxy_node) { + dev_err(dev, "Cannot find proxy node\n"); + return NULL; + } + + proxy_pdev = of_find_device_by_node(proxy_node); + if (!proxy_pdev) { + of_node_put(proxy_node); + dev_err(dev, "Cannot find device represented by proxy node\n"); + return NULL; + } + + proxy_dev = dev_get_drvdata(&proxy_pdev->dev); + + of_node_put(proxy_node); + + return proxy_dev; +} + +static int dpaa_eth_macless_probe(struct platform_device *_of_dev) +{ + int err = 0, i, channel; + struct device *dev; + struct device_node *dpa_node; + struct dpa_bp *dpa_bp; + size_t count; + struct net_device *net_dev = NULL; + struct dpa_priv_s *priv = NULL; + struct dpa_percpu_priv_s *percpu_priv; + static struct proxy_device *proxy_dev; + struct task_struct *kth; + static u8 macless_idx; + + dev = &_of_dev->dev; + + dpa_node = dev->of_node; + + if (!of_device_is_available(dpa_node)) + return -ENODEV; + + /* Get the buffer pools assigned to this interface */ + dpa_bp = dpa_bp_probe(_of_dev, &count); + if (IS_ERR(dpa_bp)) + return PTR_ERR(dpa_bp); + + for (i = 0; i < count; i++) + dpa_bp[i].seed_cb = dpa_bp_shared_port_seed; + + proxy_dev = dpa_macless_proxy_probe(_of_dev); + + + /* Allocate this early, so we can store relevant information in + * the private area (needed by 1588 code in dpa_mac_probe) + */ + net_dev = alloc_etherdev_mq(sizeof(*priv), DPAA_ETH_TX_QUEUES); + if (!net_dev) { + dev_err(dev, "alloc_etherdev_mq() failed\n"); + return -ENOMEM; + } + + /* Do this here, so we can be verbose early */ + SET_NETDEV_DEV(net_dev, dev); + dev_set_drvdata(dev, net_dev); + + priv = netdev_priv(net_dev); + priv->net_dev = net_dev; + sprintf(priv->if_type, "macless%d", macless_idx++); + + priv->msg_enable = netif_msg_init(advanced_debug, -1); + + priv->peer = NULL; + priv->mac_dev = NULL; + if (proxy_dev) { + /* This is a temporary solution for the need of + * having main driver upstreamability: adjust_link + * is a general function that should work for both + * private driver and macless driver with MAC device + * control capabilities even if the last will not be + * upstreamable. + * TODO: find a convenient solution (wrapper over + * main priv structure, etc.) + */ + priv->mac_dev = proxy_dev->mac_dev; + + /* control over proxy's mac device */ + priv->peer = (void *)proxy_dev; + } + + INIT_LIST_HEAD(&priv->dpa_fq_list); + + err = dpa_fq_probe_macless(dev, &priv->dpa_fq_list, RX); + if (!err) + err = dpa_fq_probe_macless(dev, &priv->dpa_fq_list, + TX); + if (err < 0) + goto fq_probe_failed; + + /* bp init */ + priv->bp_count = count; + err = dpa_bp_create(net_dev, dpa_bp, count); + if (err < 0) + goto bp_create_failed; + + channel = dpa_get_channel(); + + if (channel < 0) { + err = channel; + goto get_channel_failed; + } + + priv->channel = (uint16_t)channel; + + /* Start a thread that will walk the cpus with affine portals + * and add this pool channel to each's dequeue mask. + */ + kth = kthread_run(dpaa_eth_add_channel, + (void *)(unsigned long)priv->channel, + "dpaa_%p:%d", net_dev, priv->channel); + if (!kth) { + err = -ENOMEM; + goto add_channel_failed; + } + + dpa_fq_setup(priv, &shared_fq_cbs, NULL); + + /* Add the FQs to the interface, and make them active */ + /* For MAC-less devices we only get here for RX frame queues + * initialization, which are the TX queues of the other + * partition. + * It is safe to rely on one partition to set the FQ taildrop + * threshold for the TX queues of the other partition + * because the ERN notifications will be received by the + * partition doing qman_enqueue. + */ + err = dpa_fqs_init(dev, &priv->dpa_fq_list, true); + if (err < 0) + goto fq_alloc_failed; + + priv->tx_headroom = DPA_DEFAULT_TX_HEADROOM; + + priv->percpu_priv = devm_alloc_percpu(dev, *priv->percpu_priv); + + if (priv->percpu_priv == NULL) { + dev_err(dev, "devm_alloc_percpu() failed\n"); + err = -ENOMEM; + goto alloc_percpu_failed; + } + for_each_possible_cpu(i) { + percpu_priv = per_cpu_ptr(priv->percpu_priv, i); + memset(percpu_priv, 0, sizeof(*percpu_priv)); + } + + err = dpa_macless_netdev_init(dpa_node, net_dev); + if (err < 0) + goto netdev_init_failed; + + dpaa_eth_sysfs_init(&net_dev->dev); + + pr_info("fsl_dpa_macless: Probed %s interface as %s\n", + priv->if_type, net_dev->name); + + return 0; + +netdev_init_failed: +alloc_percpu_failed: +fq_alloc_failed: + if (net_dev) + dpa_fq_free(dev, &priv->dpa_fq_list); +add_channel_failed: +get_channel_failed: + if (net_dev) + dpa_bp_free(priv); +bp_create_failed: +fq_probe_failed: + dev_set_drvdata(dev, NULL); + if (net_dev) + free_netdev(net_dev); + + return err; +} + +static int __init __cold dpa_macless_load(void) +{ + int _errno; + + pr_info(DPA_DESCRIPTION "\n"); + + /* Initialize dpaa_eth mirror values */ + dpa_rx_extra_headroom = fm_get_rx_extra_headroom(); + dpa_max_frm = fm_get_max_frm(); + + _errno = platform_driver_register(&dpa_macless_driver); + if (unlikely(_errno < 0)) { + pr_err(KBUILD_MODNAME + ": %s:%hu:%s(): platform_driver_register() = %d\n", + KBUILD_BASENAME".c", __LINE__, __func__, _errno); + } + + pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", + KBUILD_BASENAME".c", __func__); + + return _errno; +} +module_init(dpa_macless_load); + +static void __exit __cold dpa_macless_unload(void) +{ + platform_driver_unregister(&dpa_macless_driver); + + pr_debug(KBUILD_MODNAME ": %s:%s() ->\n", + KBUILD_BASENAME".c", __func__); +} +module_exit(dpa_macless_unload); -- cgit v1.2.3