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.
This commit is contained in:
Sebastian Huber 2017-10-27 11:50:01 +02:00
parent 0f6ff4a923
commit f5ed3aa620
4 changed files with 1042 additions and 0 deletions

View File

@ -0,0 +1,265 @@
#include <machine/rtems-bsd-kernel-space.h>
/* 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 <linux/init.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of_platform.h>
#include <linux/of_net.h>
#include <linux/etherdevice.h>
#include <linux/kthread.h>
#include <linux/percpu.h>
#include <linux/highmem.h>
#include <linux/sort.h>
#include <linux/fsl_qman.h>
#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);

View File

@ -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 <linux/etherdevice.h> /* struct net_device */
#include <linux/fsl_bman.h> /* struct bm_buffer */
#include <linux/of_platform.h> /* struct platform_device */
#include <linux/net_tstamp.h> /* 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 */

View File

@ -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 <linux/etherdevice.h> /* struct net_device */
#include <linux/fsl_bman.h> /* struct bm_buffer */
#include <linux/of_platform.h> /* struct platform_device */
#include <linux/net_tstamp.h> /* 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, &param); \
}
#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 */

View File

@ -0,0 +1,498 @@
#include <machine/rtems-bsd-kernel-space.h>
/* 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 <linux/init.h>
#include <linux/module.h>
#include <linux/of_platform.h>
#include <linux/of_net.h>
#include <linux/etherdevice.h>
#include <linux/kthread.h>
#include <linux/percpu.h>
#include <linux/highmem.h>
#include <linux/fsl_qman.h>
#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);