From dd0cc8d0739a72ee5d85039a9ba7812383e8f555 Mon Sep 17 00:00:00 2001 From: Biwen Li Date: Wed, 17 Apr 2019 18:58:30 +0800 Subject: [PATCH] dpaa2-mac-phy: support layerscape This is an integrated patch of dpaa2-mac-phy for layerscape Signed-off-by: Alex Marginean Signed-off-by: Biwen Li Signed-off-by: Bogdan Hamciuc Signed-off-by: Bogdan Purcareata Signed-off-by: Catalin Neacsu Signed-off-by: Constantin Tudor Signed-off-by: Florin Chiculita Signed-off-by: Ioana Ciornei Signed-off-by: Ioana Radulescu Signed-off-by: Itai Katz Signed-off-by: J. German Rivera Signed-off-by: Pankaj Bansal Signed-off-by: Razvan Stefanescu Signed-off-by: Stuart Yoder Signed-off-by: Valentin Catalin Neacsu --- drivers/staging/fsl-dpaa2/mac/Kconfig | 23 + drivers/staging/fsl-dpaa2/mac/Makefile | 10 + drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h | 196 ++++++ drivers/staging/fsl-dpaa2/mac/dpmac.c | 689 ++++++++++++++++++ drivers/staging/fsl-dpaa2/mac/dpmac.h | 374 ++++++++++ drivers/staging/fsl-dpaa2/mac/mac.c | 817 ++++++++++++++++++++++ 6 files changed, 2109 insertions(+) create mode 100644 drivers/staging/fsl-dpaa2/mac/Kconfig create mode 100644 drivers/staging/fsl-dpaa2/mac/Makefile create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac.c create mode 100644 drivers/staging/fsl-dpaa2/mac/dpmac.h create mode 100644 drivers/staging/fsl-dpaa2/mac/mac.c --- /dev/null +++ b/drivers/staging/fsl-dpaa2/mac/Kconfig @@ -0,0 +1,23 @@ +config FSL_DPAA2_MAC + tristate "DPAA2 MAC / PHY interface" + depends on FSL_MC_BUS && FSL_DPAA2 + select MDIO_BUS_MUX_MMIOREG + select FSL_XGMAC_MDIO + select FIXED_PHY + ---help--- + Prototype driver for DPAA2 MAC / PHY interface object. + This driver works as a proxy between phylib including phy drivers and + the MC firmware. It receives updates on link state changes from PHY + lib and forwards them to MC and receives interrupt from MC whenever + a request is made to change the link state. + + +config FSL_DPAA2_MAC_NETDEVS + bool "Expose net interfaces for PHYs" + default n + depends on FSL_DPAA2_MAC + ---help--- + Exposes macX net interfaces which allow direct control over MACs and + PHYs. + . + Leave disabled if unsure. --- /dev/null +++ b/drivers/staging/fsl-dpaa2/mac/Makefile @@ -0,0 +1,10 @@ + +obj-$(CONFIG_FSL_DPAA2_MAC) += dpaa2-mac.o + +dpaa2-mac-objs := mac.o dpmac.o + +all: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean --- /dev/null +++ b/drivers/staging/fsl-dpaa2/mac/dpmac-cmd.h @@ -0,0 +1,196 @@ +/* Copyright 2013-2016 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 the above-listed copyright holders nor the + * names of any 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS 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 _FSL_DPMAC_CMD_H +#define _FSL_DPMAC_CMD_H + +/* DPMAC Version */ +#define DPMAC_VER_MAJOR 4 +#define DPMAC_VER_MINOR 2 +#define DPMAC_CMD_BASE_VERSION 1 +#define DPMAC_CMD_2ND_VERSION 2 +#define DPMAC_CMD_ID_OFFSET 4 + +#define DPMAC_CMD(id) (((id) << DPMAC_CMD_ID_OFFSET) | DPMAC_CMD_BASE_VERSION) +#define DPMAC_CMD_V2(id) (((id) << DPMAC_CMD_ID_OFFSET) | DPMAC_CMD_2ND_VERSION) + +/* Command IDs */ +#define DPMAC_CMDID_CLOSE DPMAC_CMD(0x800) +#define DPMAC_CMDID_OPEN DPMAC_CMD(0x80c) +#define DPMAC_CMDID_CREATE DPMAC_CMD(0x90c) +#define DPMAC_CMDID_DESTROY DPMAC_CMD(0x98c) +#define DPMAC_CMDID_GET_API_VERSION DPMAC_CMD(0xa0c) + +#define DPMAC_CMDID_GET_ATTR DPMAC_CMD(0x004) +#define DPMAC_CMDID_RESET DPMAC_CMD(0x005) + +#define DPMAC_CMDID_SET_IRQ_ENABLE DPMAC_CMD(0x012) +#define DPMAC_CMDID_GET_IRQ_ENABLE DPMAC_CMD(0x013) +#define DPMAC_CMDID_SET_IRQ_MASK DPMAC_CMD(0x014) +#define DPMAC_CMDID_GET_IRQ_MASK DPMAC_CMD(0x015) +#define DPMAC_CMDID_GET_IRQ_STATUS DPMAC_CMD(0x016) +#define DPMAC_CMDID_CLEAR_IRQ_STATUS DPMAC_CMD(0x017) + +#define DPMAC_CMDID_GET_LINK_CFG DPMAC_CMD(0x0c2) +#define DPMAC_CMDID_GET_LINK_CFG_V2 DPMAC_CMD_V2(0x0c2) +#define DPMAC_CMDID_SET_LINK_STATE DPMAC_CMD(0x0c3) +#define DPMAC_CMDID_SET_LINK_STATE_V2 DPMAC_CMD_V2(0x0c3) +#define DPMAC_CMDID_GET_COUNTER DPMAC_CMD(0x0c4) + +#define DPMAC_CMDID_SET_PORT_MAC_ADDR DPMAC_CMD(0x0c5) + +/* Macros for accessing command fields smaller than 1byte */ +#define DPMAC_MASK(field) \ + GENMASK(DPMAC_##field##_SHIFT + DPMAC_##field##_SIZE - 1, \ + DPMAC_##field##_SHIFT) +#define dpmac_set_field(var, field, val) \ + ((var) |= (((val) << DPMAC_##field##_SHIFT) & DPMAC_MASK(field))) +#define dpmac_get_field(var, field) \ + (((var) & DPMAC_MASK(field)) >> DPMAC_##field##_SHIFT) + +struct dpmac_cmd_open { + u32 dpmac_id; +}; + +struct dpmac_cmd_create { + u32 mac_id; +}; + +struct dpmac_cmd_destroy { + u32 dpmac_id; +}; + +struct dpmac_cmd_set_irq_enable { + u8 enable; + u8 pad[3]; + u8 irq_index; +}; + +struct dpmac_cmd_get_irq_enable { + u32 pad; + u8 irq_index; +}; + +struct dpmac_rsp_get_irq_enable { + u8 enabled; +}; + +struct dpmac_cmd_set_irq_mask { + u32 mask; + u8 irq_index; +}; + +struct dpmac_cmd_get_irq_mask { + u32 pad; + u8 irq_index; +}; + +struct dpmac_rsp_get_irq_mask { + u32 mask; +}; + +struct dpmac_cmd_get_irq_status { + u32 status; + u8 irq_index; +}; + +struct dpmac_rsp_get_irq_status { + u32 status; +}; + +struct dpmac_cmd_clear_irq_status { + u32 status; + u8 irq_index; +}; + +struct dpmac_rsp_get_attributes { + u8 eth_if; + u8 link_type; + u16 id; + u32 max_rate; +}; + +struct dpmac_rsp_get_link_cfg { + u64 options; + u32 rate; +}; + +struct dpmac_rsp_get_link_cfg_v2 { + u64 options; + u32 rate; + u32 pad; + u64 advertising; +}; + +#define DPMAC_STATE_SIZE 1 +#define DPMAC_STATE_SHIFT 0 +#define DPMAC_STATE_VALID_SIZE 1 +#define DPMAC_STATE_VALID_SHIFT 1 + +struct dpmac_cmd_set_link_state { + u64 options; + u32 rate; + u32 pad; + /* only least significant bit is valid */ + u8 up; +}; + +struct dpmac_cmd_set_link_state_v2 { + u64 options; + u32 rate; + u32 pad0; + /* from lsb: up:1, state_valid:1 */ + u8 state; + u8 pad1[7]; + u64 supported; + u64 advertising; +}; + +struct dpmac_cmd_get_counter { + u8 type; +}; + +struct dpmac_rsp_get_counter { + u64 pad; + u64 counter; +}; + +struct dpmac_rsp_get_api_version { + u16 major; + u16 minor; +}; + +struct dpmac_cmd_set_port_mac_addr { + u8 pad[2]; + u8 addr[6]; +}; + +#endif /* _FSL_DPMAC_CMD_H */ --- /dev/null +++ b/drivers/staging/fsl-dpaa2/mac/dpmac.c @@ -0,0 +1,689 @@ +/* Copyright 2013-2016 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 the above-listed copyright holders nor the + * names of any 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS 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. + */ +#include +#include "dpmac.h" +#include "dpmac-cmd.h" + +/** + * dpmac_open() - Open a control session for the specified object. + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @dpmac_id: DPMAC unique ID + * @token: Returned token; use in subsequent API calls + * + * This function can be used to open a control session for an + * already created object; an object may have been declared in + * the DPL or by calling the dpmac_create function. + * This function returns a unique authentication token, + * associated with the specific object ID and the specific MC + * portal; this token must be used in all subsequent commands for + * this specific object + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_open(struct fsl_mc_io *mc_io, + u32 cmd_flags, + int dpmac_id, + u16 *token) +{ + struct dpmac_cmd_open *cmd_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_OPEN, + cmd_flags, + 0); + cmd_params = (struct dpmac_cmd_open *)cmd.params; + cmd_params->dpmac_id = cpu_to_le32(dpmac_id); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + *token = mc_cmd_hdr_read_token(&cmd); + + return err; +} + +/** + * dpmac_close() - Close the control session of the object + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * + * After this function is called, no further operations are + * allowed on the object without opening a new control session. + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_close(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token) +{ + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLOSE, cmd_flags, + token); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_create() - Create the DPMAC object. + * @mc_io: Pointer to MC portal's I/O object + * @dprc_token: Parent container token; '0' for default container + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @cfg: Configuration structure + * @obj_id: Returned object id + * + * Create the DPMAC object, allocate required resources and + * perform required initialization. + * + * The function accepts an authentication token of a parent + * container that this object should be assigned to. The token + * can be '0' so the object will be assigned to the default container. + * The newly created object can be opened with the returned + * object id and using the container's associated tokens and MC portals. + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_create(struct fsl_mc_io *mc_io, + u16 dprc_token, + u32 cmd_flags, + const struct dpmac_cfg *cfg, + u32 *obj_id) +{ + struct dpmac_cmd_create *cmd_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CREATE, + cmd_flags, + dprc_token); + cmd_params = (struct dpmac_cmd_create *)cmd.params; + cmd_params->mac_id = cpu_to_le32(cfg->mac_id); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + *obj_id = mc_cmd_read_object_id(&cmd); + + return 0; +} + +/** + * dpmac_destroy() - Destroy the DPMAC object and release all its resources. + * @mc_io: Pointer to MC portal's I/O object + * @dprc_token: Parent container token; '0' for default container + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @object_id: The object id; it must be a valid id within the container that + * created this object; + * + * The function accepts the authentication token of the parent container that + * created the object (not the one that currently owns the object). The object + * is searched within parent using the provided 'object_id'. + * All tokens to the object must be closed before calling destroy. + * + * Return: '0' on Success; error code otherwise. + */ +int dpmac_destroy(struct fsl_mc_io *mc_io, + u16 dprc_token, + u32 cmd_flags, + u32 object_id) +{ + struct dpmac_cmd_destroy *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_DESTROY, + cmd_flags, + dprc_token); + cmd_params = (struct dpmac_cmd_destroy *)cmd.params; + cmd_params->dpmac_id = cpu_to_le32(object_id); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_set_irq_enable() - Set overall interrupt state. + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @irq_index: The interrupt index to configure + * @en: Interrupt state - enable = 1, disable = 0 + * + * Allows GPP software to control when interrupts are generated. + * Each interrupt can have up to 32 causes. The enable/disable control's the + * overall interrupt state. if the interrupt is disabled no causes will cause + * an interrupt. + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u8 en) +{ + struct dpmac_cmd_set_irq_enable *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_ENABLE, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_set_irq_enable *)cmd.params; + cmd_params->irq_index = irq_index; + cmd_params->enable = en; + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_get_irq_enable() - Get overall interrupt state + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @irq_index: The interrupt index to configure + * @en: Returned interrupt state - enable = 1, disable = 0 + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u8 *en) +{ + struct dpmac_cmd_get_irq_enable *cmd_params; + struct dpmac_rsp_get_irq_enable *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_ENABLE, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_get_irq_enable *)cmd.params; + cmd_params->irq_index = irq_index; + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + rsp_params = (struct dpmac_rsp_get_irq_enable *)cmd.params; + *en = rsp_params->enabled; + + return 0; +} + +/** + * dpmac_set_irq_mask() - Set interrupt mask. + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @irq_index: The interrupt index to configure + * @mask: Event mask to trigger interrupt; + * each bit: + * 0 = ignore event + * 1 = consider event for asserting IRQ + * + * Every interrupt can have up to 32 causes and the interrupt model supports + * masking/unmasking each cause independently + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 mask) +{ + struct dpmac_cmd_set_irq_mask *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_IRQ_MASK, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_set_irq_mask *)cmd.params; + cmd_params->mask = cpu_to_le32(mask); + cmd_params->irq_index = irq_index; + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_get_irq_mask() - Get interrupt mask. + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @irq_index: The interrupt index to configure + * @mask: Returned event mask to trigger interrupt + * + * Every interrupt can have up to 32 causes and the interrupt model supports + * masking/unmasking each cause independently + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 *mask) +{ + struct dpmac_cmd_get_irq_mask *cmd_params; + struct dpmac_rsp_get_irq_mask *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_MASK, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_get_irq_mask *)cmd.params; + cmd_params->irq_index = irq_index; + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + rsp_params = (struct dpmac_rsp_get_irq_mask *)cmd.params; + *mask = le32_to_cpu(rsp_params->mask); + + return 0; +} + +/** + * dpmac_get_irq_status() - Get the current status of any pending interrupts. + * + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @irq_index: The interrupt index to configure + * @status: Returned interrupts status - one bit per cause: + * 0 = no interrupt pending + * 1 = interrupt pending + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_irq_status(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 *status) +{ + struct dpmac_cmd_get_irq_status *cmd_params; + struct dpmac_rsp_get_irq_status *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_IRQ_STATUS, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_get_irq_status *)cmd.params; + cmd_params->status = cpu_to_le32(*status); + cmd_params->irq_index = irq_index; + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + rsp_params = (struct dpmac_rsp_get_irq_status *)cmd.params; + *status = le32_to_cpu(rsp_params->status); + + return 0; +} + +/** + * dpmac_clear_irq_status() - Clear a pending interrupt's status + * + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @irq_index: The interrupt index to configure + * @status: Bits to clear (W1C) - one bit per cause: + * 0 = don't change + * 1 = clear status bit + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 status) +{ + struct dpmac_cmd_clear_irq_status *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_CLEAR_IRQ_STATUS, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_clear_irq_status *)cmd.params; + cmd_params->status = cpu_to_le32(status); + cmd_params->irq_index = irq_index; + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_get_attributes - Retrieve DPMAC attributes. + * + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @attr: Returned object's attributes + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_attributes(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_attr *attr) +{ + struct dpmac_rsp_get_attributes *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_ATTR, + cmd_flags, + token); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + /* retrieve response parameters */ + rsp_params = (struct dpmac_rsp_get_attributes *)cmd.params; + attr->eth_if = rsp_params->eth_if; + attr->link_type = rsp_params->link_type; + attr->id = le16_to_cpu(rsp_params->id); + attr->max_rate = le32_to_cpu(rsp_params->max_rate); + + return 0; +} + +/** + * dpmac_get_link_cfg() - Get Ethernet link configuration + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @cfg: Returned structure with the link configuration + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_cfg *cfg) +{ + struct dpmac_rsp_get_link_cfg *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err = 0; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_LINK_CFG, + cmd_flags, + token); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpmac_rsp_get_link_cfg *)cmd.params; + cfg->options = le64_to_cpu(rsp_params->options); + cfg->rate = le32_to_cpu(rsp_params->rate); + + return 0; +} + +/** + * dpmac_get_link_cfg_v2() - Get Ethernet link configuration + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @cfg: Returned structure with the link configuration + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_link_cfg_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_cfg *cfg) +{ + struct dpmac_rsp_get_link_cfg_v2 *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err = 0; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_LINK_CFG_V2, + cmd_flags, + token); + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpmac_rsp_get_link_cfg_v2 *)cmd.params; + cfg->options = le64_to_cpu(rsp_params->options); + cfg->rate = le32_to_cpu(rsp_params->rate); + cfg->advertising = le64_to_cpu(rsp_params->advertising); + + return 0; +} + +/** + * dpmac_set_link_state() - Set the Ethernet link status + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @link_state: Link state configuration + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_set_link_state(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_state *link_state) +{ + struct dpmac_cmd_set_link_state *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_LINK_STATE, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_set_link_state *)cmd.params; + cmd_params->options = cpu_to_le64(link_state->options); + cmd_params->rate = cpu_to_le32(link_state->rate); + dpmac_set_field(cmd_params->up, STATE, link_state->up); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_set_link_state_v2() - Set the Ethernet link status + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @link_state: Link state configuration + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_set_link_state_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_state *link_state) +{ + struct dpmac_cmd_set_link_state_v2 *cmd_params; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_LINK_STATE_V2, + cmd_flags, + token); + cmd_params = (struct dpmac_cmd_set_link_state_v2 *)cmd.params; + cmd_params->options = cpu_to_le64(link_state->options); + cmd_params->rate = cpu_to_le32(link_state->rate); + dpmac_set_field(cmd_params->state, STATE, link_state->up); + dpmac_set_field(cmd_params->state, STATE_VALID, + link_state->state_valid); + cmd_params->supported = cpu_to_le64(link_state->supported); + cmd_params->advertising = cpu_to_le64(link_state->advertising); + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_get_counter() - Read a specific DPMAC counter + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @type: The requested counter + * @counter: Returned counter value + * + * Return: The requested counter; '0' otherwise. + */ +int dpmac_get_counter(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + enum dpmac_counter type, + u64 *counter) +{ + struct dpmac_cmd_get_counter *dpmac_cmd; + struct dpmac_rsp_get_counter *dpmac_rsp; + struct fsl_mc_command cmd = { 0 }; + int err = 0; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_COUNTER, + cmd_flags, + token); + dpmac_cmd = (struct dpmac_cmd_get_counter *)cmd.params; + dpmac_cmd->type = type; + + /* send command to mc*/ + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + dpmac_rsp = (struct dpmac_rsp_get_counter *)cmd.params; + *counter = le64_to_cpu(dpmac_rsp->counter); + + return 0; +} + +/* untested */ +int dpmac_set_port_mac_addr(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const u8 addr[6]) +{ + struct dpmac_cmd_set_port_mac_addr *dpmac_cmd; + struct fsl_mc_command cmd = { 0 }; + + /* prepare command */ + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_SET_PORT_MAC_ADDR, + cmd_flags, + token); + dpmac_cmd = (struct dpmac_cmd_set_port_mac_addr *)cmd.params; + dpmac_cmd->addr[0] = addr[5]; + dpmac_cmd->addr[1] = addr[4]; + dpmac_cmd->addr[2] = addr[3]; + dpmac_cmd->addr[3] = addr[2]; + dpmac_cmd->addr[4] = addr[1]; + dpmac_cmd->addr[5] = addr[0]; + + /* send command to mc*/ + return mc_send_command(mc_io, &cmd); +} + +/** + * dpmac_get_api_version() - Get Data Path MAC version + * @mc_io: Pointer to MC portal's I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @major_ver: Major version of data path mac API + * @minor_ver: Minor version of data path mac API + * + * Return: '0' on Success; Error code otherwise. + */ +int dpmac_get_api_version(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 *major_ver, + u16 *minor_ver) +{ + struct dpmac_rsp_get_api_version *rsp_params; + struct fsl_mc_command cmd = { 0 }; + int err; + + cmd.header = mc_encode_cmd_header(DPMAC_CMDID_GET_API_VERSION, + cmd_flags, + 0); + + err = mc_send_command(mc_io, &cmd); + if (err) + return err; + + rsp_params = (struct dpmac_rsp_get_api_version *)cmd.params; + *major_ver = le16_to_cpu(rsp_params->major); + *minor_ver = le16_to_cpu(rsp_params->minor); + + return 0; +} --- /dev/null +++ b/drivers/staging/fsl-dpaa2/mac/dpmac.h @@ -0,0 +1,374 @@ +/* Copyright 2013-2016 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 the above-listed copyright holders nor the + * names of any 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDERS OR CONTRIBUTORS 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 __FSL_DPMAC_H +#define __FSL_DPMAC_H + +/* Data Path MAC API + * Contains initialization APIs and runtime control APIs for DPMAC + */ + +struct fsl_mc_io; + +int dpmac_open(struct fsl_mc_io *mc_io, + u32 cmd_flags, + int dpmac_id, + u16 *token); + +int dpmac_close(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token); + +/** + * enum dpmac_link_type - DPMAC link type + * @DPMAC_LINK_TYPE_NONE: No link + * @DPMAC_LINK_TYPE_FIXED: Link is fixed type + * @DPMAC_LINK_TYPE_PHY: Link by PHY ID + * @DPMAC_LINK_TYPE_BACKPLANE: Backplane link type + */ +enum dpmac_link_type { + DPMAC_LINK_TYPE_NONE, + DPMAC_LINK_TYPE_FIXED, + DPMAC_LINK_TYPE_PHY, + DPMAC_LINK_TYPE_BACKPLANE +}; + +/** + * enum dpmac_eth_if - DPMAC Ethrnet interface + * @DPMAC_ETH_IF_MII: MII interface + * @DPMAC_ETH_IF_RMII: RMII interface + * @DPMAC_ETH_IF_SMII: SMII interface + * @DPMAC_ETH_IF_GMII: GMII interface + * @DPMAC_ETH_IF_RGMII: RGMII interface + * @DPMAC_ETH_IF_SGMII: SGMII interface + * @DPMAC_ETH_IF_QSGMII: QSGMII interface + * @DPMAC_ETH_IF_XAUI: XAUI interface + * @DPMAC_ETH_IF_XFI: XFI interface + */ +enum dpmac_eth_if { + DPMAC_ETH_IF_MII, + DPMAC_ETH_IF_RMII, + DPMAC_ETH_IF_SMII, + DPMAC_ETH_IF_GMII, + DPMAC_ETH_IF_RGMII, + DPMAC_ETH_IF_SGMII, + DPMAC_ETH_IF_QSGMII, + DPMAC_ETH_IF_XAUI, + DPMAC_ETH_IF_XFI +}; + +/** + * struct dpmac_cfg - Structure representing DPMAC configuration + * @mac_id: Represents the Hardware MAC ID; in case of multiple WRIOP, + * the MAC IDs are continuous. + * For example: 2 WRIOPs, 16 MACs in each: + * MAC IDs for the 1st WRIOP: 1-16, + * MAC IDs for the 2nd WRIOP: 17-32. + */ +struct dpmac_cfg { + u16 mac_id; +}; + +int dpmac_create(struct fsl_mc_io *mc_io, + u16 dprc_token, + u32 cmd_flags, + const struct dpmac_cfg *cfg, + u32 *obj_id); + +int dpmac_destroy(struct fsl_mc_io *mc_io, + u16 dprc_token, + u32 cmd_flags, + u32 object_id); + +/** + * DPMAC IRQ Index and Events + */ + +/** + * IRQ index + */ +#define DPMAC_IRQ_INDEX 0 +/** + * IRQ event - indicates a change in link state + */ +#define DPMAC_IRQ_EVENT_LINK_CFG_REQ 0x00000001 +/** + * IRQ event - Indicates that the link state changed + */ +#define DPMAC_IRQ_EVENT_LINK_CHANGED 0x00000002 + +int dpmac_set_irq_enable(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u8 en); + +int dpmac_get_irq_enable(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u8 *en); + +int dpmac_set_irq_mask(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 mask); + +int dpmac_get_irq_mask(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 *mask); + +int dpmac_get_irq_status(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 *status); + +int dpmac_clear_irq_status(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + u8 irq_index, + u32 status); + +/** + * struct dpmac_attr - Structure representing DPMAC attributes + * @id: DPMAC object ID + * @max_rate: Maximum supported rate - in Mbps + * @eth_if: Ethernet interface + * @link_type: link type + */ +struct dpmac_attr { + u16 id; + u32 max_rate; + enum dpmac_eth_if eth_if; + enum dpmac_link_type link_type; +}; + +int dpmac_get_attributes(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_attr *attr); + +/** + * DPMAC link configuration/state options + */ + +/** + * Enable auto-negotiation + */ +#define DPMAC_LINK_OPT_AUTONEG 0x0000000000000001ULL +/** + * Enable half-duplex mode + */ +#define DPMAC_LINK_OPT_HALF_DUPLEX 0x0000000000000002ULL +/** + * Enable pause frames + */ +#define DPMAC_LINK_OPT_PAUSE 0x0000000000000004ULL +/** + * Enable a-symmetric pause frames + */ +#define DPMAC_LINK_OPT_ASYM_PAUSE 0x0000000000000008ULL + +/** + * Advertised link speeds + */ +#define DPMAC_ADVERTISED_10BASET_FULL 0x0000000000000001ULL +#define DPMAC_ADVERTISED_100BASET_FULL 0x0000000000000002ULL +#define DPMAC_ADVERTISED_1000BASET_FULL 0x0000000000000004ULL +#define DPMAC_ADVERTISED_10000BASET_FULL 0x0000000000000010ULL +#define DPMAC_ADVERTISED_2500BASEX_FULL 0x0000000000000020ULL + +/** + * Advertise auto-negotiation enable + */ +#define DPMAC_ADVERTISED_AUTONEG 0x0000000000000008ULL + +/** + * struct dpmac_link_cfg - Structure representing DPMAC link configuration + * @rate: Link's rate - in Mbps + * @options: Enable/Disable DPMAC link cfg features (bitmap) + * @advertising: Speeds that are advertised for autoneg (bitmap) + */ +struct dpmac_link_cfg { + u32 rate; + u64 options; + u64 advertising; +}; + +int dpmac_get_link_cfg(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_cfg *cfg); + +int dpmac_get_link_cfg_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_cfg *cfg); + +/** + * struct dpmac_link_state - DPMAC link configuration request + * @rate: Rate in Mbps + * @options: Enable/Disable DPMAC link cfg features (bitmap) + * @up: Link state + * @state_valid: Ignore/Update the state of the link + * @supported: Speeds capability of the phy (bitmap) + * @advertising: Speeds that are advertised for autoneg (bitmap) + */ +struct dpmac_link_state { + u32 rate; + u64 options; + int up; + int state_valid; + u64 supported; + u64 advertising; +}; + +int dpmac_set_link_state(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_state *link_state); + +int dpmac_set_link_state_v2(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + struct dpmac_link_state *link_state); + +/** + * enum dpmac_counter - DPMAC counter types + * @DPMAC_CNT_ING_FRAME_64: counts 64-bytes frames, good or bad. + * @DPMAC_CNT_ING_FRAME_127: counts 65- to 127-bytes frames, good or bad. + * @DPMAC_CNT_ING_FRAME_255: counts 128- to 255-bytes frames, good or bad. + * @DPMAC_CNT_ING_FRAME_511: counts 256- to 511-bytes frames, good or bad. + * @DPMAC_CNT_ING_FRAME_1023: counts 512- to 1023-bytes frames, good or bad. + * @DPMAC_CNT_ING_FRAME_1518: counts 1024- to 1518-bytes frames, good or bad. + * @DPMAC_CNT_ING_FRAME_1519_MAX: counts 1519-bytes frames and larger + * (up to max frame length specified), + * good or bad. + * @DPMAC_CNT_ING_FRAG: counts frames which are shorter than 64 bytes received + * with a wrong CRC + * @DPMAC_CNT_ING_JABBER: counts frames longer than the maximum frame length + * specified, with a bad frame check sequence. + * @DPMAC_CNT_ING_FRAME_DISCARD: counts dropped frames due to internal errors. + * Occurs when a receive FIFO overflows. + * Includes also frames truncated as a result of + * the receive FIFO overflow. + * @DPMAC_CNT_ING_ALIGN_ERR: counts frames with an alignment error + * (optional used for wrong SFD). + * @DPMAC_CNT_EGR_UNDERSIZED: counts frames transmitted that was less than 64 + * bytes long with a good CRC. + * @DPMAC_CNT_ING_OVERSIZED: counts frames longer than the maximum frame length + * specified, with a good frame check sequence. + * @DPMAC_CNT_ING_VALID_PAUSE_FRAME: counts valid pause frames (regular and PFC) + * @DPMAC_CNT_EGR_VALID_PAUSE_FRAME: counts valid pause frames transmitted + * (regular and PFC). + * @DPMAC_CNT_ING_BYTE: counts bytes received except preamble for all valid + * frames and valid pause frames. + * @DPMAC_CNT_ING_MCAST_FRAME: counts received multicast frames. + * @DPMAC_CNT_ING_BCAST_FRAME: counts received broadcast frames. + * @DPMAC_CNT_ING_ALL_FRAME: counts each good or bad frames received. + * @DPMAC_CNT_ING_UCAST_FRAME: counts received unicast frames. + * @DPMAC_CNT_ING_ERR_FRAME: counts frames received with an error + * (except for undersized/fragment frame). + * @DPMAC_CNT_EGR_BYTE: counts bytes transmitted except preamble for all valid + * frames and valid pause frames transmitted. + * @DPMAC_CNT_EGR_MCAST_FRAME: counts transmitted multicast frames. + * @DPMAC_CNT_EGR_BCAST_FRAME: counts transmitted broadcast frames. + * @DPMAC_CNT_EGR_UCAST_FRAME: counts transmitted unicast frames. + * @DPMAC_CNT_EGR_ERR_FRAME: counts frames transmitted with an error. + * @DPMAC_CNT_ING_GOOD_FRAME: counts frames received without error, including + * pause frames. + * @DPMAC_CNT_ENG_GOOD_FRAME: counts frames transmitted without error, including + * pause frames. + */ +enum dpmac_counter { + DPMAC_CNT_ING_FRAME_64, + DPMAC_CNT_ING_FRAME_127, + DPMAC_CNT_ING_FRAME_255, + DPMAC_CNT_ING_FRAME_511, + DPMAC_CNT_ING_FRAME_1023, + DPMAC_CNT_ING_FRAME_1518, + DPMAC_CNT_ING_FRAME_1519_MAX, + DPMAC_CNT_ING_FRAG, + DPMAC_CNT_ING_JABBER, + DPMAC_CNT_ING_FRAME_DISCARD, + DPMAC_CNT_ING_ALIGN_ERR, + DPMAC_CNT_EGR_UNDERSIZED, + DPMAC_CNT_ING_OVERSIZED, + DPMAC_CNT_ING_VALID_PAUSE_FRAME, + DPMAC_CNT_EGR_VALID_PAUSE_FRAME, + DPMAC_CNT_ING_BYTE, + DPMAC_CNT_ING_MCAST_FRAME, + DPMAC_CNT_ING_BCAST_FRAME, + DPMAC_CNT_ING_ALL_FRAME, + DPMAC_CNT_ING_UCAST_FRAME, + DPMAC_CNT_ING_ERR_FRAME, + DPMAC_CNT_EGR_BYTE, + DPMAC_CNT_EGR_MCAST_FRAME, + DPMAC_CNT_EGR_BCAST_FRAME, + DPMAC_CNT_EGR_UCAST_FRAME, + DPMAC_CNT_EGR_ERR_FRAME, + DPMAC_CNT_ING_GOOD_FRAME, + DPMAC_CNT_ENG_GOOD_FRAME +}; + +int dpmac_get_counter(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + enum dpmac_counter type, + u64 *counter); + +/** + * dpmac_set_port_mac_addr() - Set a MAC address associated with the physical + * port. This is not used for filtering, MAC is always in + * promiscuous mode, it is passed to DPNIs through DPNI API for + * application used. + * @mc_io: Pointer to opaque I/O object + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' + * @token: Token of DPMAC object + * @addr: MAC address to set + * + * Return: The requested counter; '0' otherwise. + */ +int dpmac_set_port_mac_addr(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 token, + const u8 addr[6]); + +int dpmac_get_api_version(struct fsl_mc_io *mc_io, + u32 cmd_flags, + u16 *major_ver, + u16 *minor_ver); + +#endif /* __FSL_DPMAC_H */ --- /dev/null +++ b/drivers/staging/fsl-dpaa2/mac/mac.c @@ -0,0 +1,817 @@ +/* Copyright 2015 Freescale Semiconductor Inc. + * Copyright 2018 NXP + * + * 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. + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "dpmac.h" +#include "dpmac-cmd.h" + +struct dpaa2_mac_priv { + struct net_device *netdev; + struct fsl_mc_device *mc_dev; + struct dpmac_attr attr; + struct dpmac_link_state old_state; + u16 dpmac_ver_major; + u16 dpmac_ver_minor; +}; + +/* TODO: fix the 10G modes, mapping can't be right: + * XGMII is paralel + * XAUI is serial, using 8b/10b encoding + * XFI is also serial but using 64b/66b encoding + * they can't all map to XGMII... + * + * This must be kept in sync with enum dpmac_eth_if. + */ +static phy_interface_t dpaa2_mac_iface_mode[] = { + PHY_INTERFACE_MODE_MII, /* DPMAC_ETH_IF_MII */ + PHY_INTERFACE_MODE_RMII, /* DPMAC_ETH_IF_RMII */ + PHY_INTERFACE_MODE_SMII, /* DPMAC_ETH_IF_SMII */ + PHY_INTERFACE_MODE_GMII, /* DPMAC_ETH_IF_GMII */ + PHY_INTERFACE_MODE_RGMII, /* DPMAC_ETH_IF_RGMII */ + PHY_INTERFACE_MODE_SGMII, /* DPMAC_ETH_IF_SGMII */ + PHY_INTERFACE_MODE_QSGMII, /* DPMAC_ETH_IF_QSGMII */ + PHY_INTERFACE_MODE_XGMII, /* DPMAC_ETH_IF_XAUI */ + PHY_INTERFACE_MODE_XGMII, /* DPMAC_ETH_IF_XFI */ + PHY_INTERFACE_MODE_XGMII, /* DPMAC_ETH_IF_CAUI */ + PHY_INTERFACE_MODE_XGMII, /* DPMAC_ETH_IF_1000BASEX */ + PHY_INTERFACE_MODE_XGMII, /* DPMAC_ETH_IF_USXGMII */ +}; + +static int cmp_dpmac_ver(struct dpaa2_mac_priv *priv, + u16 ver_major, u16 ver_minor) +{ + if (priv->dpmac_ver_major == ver_major) + return priv->dpmac_ver_minor - ver_minor; + return priv->dpmac_ver_major - ver_major; +} + +#define DPMAC_LINK_AUTONEG_VER_MAJOR 4 +#define DPMAC_LINK_AUTONEG_VER_MINOR 3 + +struct dpaa2_mac_link_mode_map { + u64 dpmac_lm; + u64 ethtool_lm; +}; + +static const struct dpaa2_mac_link_mode_map dpaa2_mac_lm_map[] = { + {DPMAC_ADVERTISED_10BASET_FULL, ETHTOOL_LINK_MODE_10baseT_Full_BIT}, + {DPMAC_ADVERTISED_100BASET_FULL, ETHTOOL_LINK_MODE_100baseT_Full_BIT}, + {DPMAC_ADVERTISED_1000BASET_FULL, ETHTOOL_LINK_MODE_1000baseT_Full_BIT}, + {DPMAC_ADVERTISED_10000BASET_FULL, ETHTOOL_LINK_MODE_10000baseT_Full_BIT}, + {DPMAC_ADVERTISED_2500BASEX_FULL, ETHTOOL_LINK_MODE_2500baseX_Full_BIT}, + {DPMAC_ADVERTISED_AUTONEG, ETHTOOL_LINK_MODE_Autoneg_BIT}, +}; + +static void link_mode_dpmac2phydev(u64 dpmac_lm, u32 *phydev_lm) +{ + __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; + int i; + + for (i = 0; i < ARRAY_SIZE(dpaa2_mac_lm_map); i++) { + if (dpmac_lm & dpaa2_mac_lm_map[i].dpmac_lm) + __set_bit(dpaa2_mac_lm_map[i].ethtool_lm, mask); + } + + ethtool_convert_link_mode_to_legacy_u32(phydev_lm, mask); +} + +static void link_mode_phydev2dpmac(u32 phydev_lm, u64 *dpni_lm) +{ + unsigned long lm; + int i; + + ethtool_convert_legacy_u32_to_link_mode(&lm, phydev_lm); + + for (i = 0; i < ARRAY_SIZE(dpaa2_mac_lm_map); i++) { + if (test_bit(dpaa2_mac_lm_map[i].ethtool_lm, &lm)) + *dpni_lm |= dpaa2_mac_lm_map[i].dpmac_lm; + } +} + +static void dpaa2_mac_link_changed(struct net_device *netdev) +{ + struct phy_device *phydev; + struct dpmac_link_state state = { 0 }; + struct dpaa2_mac_priv *priv = netdev_priv(netdev); + int err; + + /* the PHY just notified us of link state change */ + phydev = netdev->phydev; + + state.up = !!phydev->link; + if (phydev->link) { + state.rate = phydev->speed; + + if (!phydev->duplex) + state.options |= DPMAC_LINK_OPT_HALF_DUPLEX; + if (phydev->autoneg) + state.options |= DPMAC_LINK_OPT_AUTONEG; + + if (phydev->pause && (phydev->advertising & ADVERTISED_Pause)) + state.options |= DPMAC_LINK_OPT_PAUSE; + if (phydev->pause && + (phydev->advertising & ADVERTISED_Asym_Pause)) + state.options |= DPMAC_LINK_OPT_ASYM_PAUSE; + + netif_carrier_on(netdev); + } else { + netif_carrier_off(netdev); + } + + if (priv->old_state.up != state.up || + priv->old_state.rate != state.rate || + priv->old_state.options != state.options) { + priv->old_state = state; + phy_print_status(phydev); + } + + if (cmp_dpmac_ver(priv, DPMAC_LINK_AUTONEG_VER_MAJOR, + DPMAC_LINK_AUTONEG_VER_MINOR) < 0) { + err = dpmac_set_link_state(priv->mc_dev->mc_io, 0, + priv->mc_dev->mc_handle, &state); + } else { + link_mode_phydev2dpmac(phydev->supported, &state.supported); + link_mode_phydev2dpmac(phydev->advertising, &state.advertising); + state.state_valid = 1; + + err = dpmac_set_link_state_v2(priv->mc_dev->mc_io, 0, + priv->mc_dev->mc_handle, &state); + } + if (unlikely(err)) + dev_err(&priv->mc_dev->dev, "dpmac_set_link_state: %d\n", err); +} + +static int dpaa2_mac_open(struct net_device *netdev) +{ + /* start PHY state machine */ + phy_start(netdev->phydev); + + return 0; +} + +static int dpaa2_mac_stop(struct net_device *netdev) +{ + if (!netdev->phydev) + goto done; + + /* stop PHY state machine */ + phy_stop(netdev->phydev); + + /* signal link down to firmware */ + netdev->phydev->link = 0; + dpaa2_mac_link_changed(netdev); + +done: + return 0; +} + +#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS +static netdev_tx_t dpaa2_mac_drop_frame(struct sk_buff *skb, + struct net_device *dev) +{ + /* we don't support I/O for now, drop the frame */ + dev_kfree_skb_any(skb); + return NETDEV_TX_OK; +} + +static void dpaa2_mac_get_drvinfo(struct net_device *net_dev, + struct ethtool_drvinfo *drvinfo) +{ + struct dpaa2_mac_priv *priv = netdev_priv(net_dev); + + strlcpy(drvinfo->driver, KBUILD_MODNAME, sizeof(drvinfo->driver)); + snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), + "%u.%u", priv->dpmac_ver_major, priv->dpmac_ver_minor); + strlcpy(drvinfo->bus_info, dev_name(net_dev->dev.parent->parent), + sizeof(drvinfo->bus_info)); +} + +static int dpaa2_mac_get_link_ksettings(struct net_device *netdev, + struct ethtool_link_ksettings *ks) +{ + phy_ethtool_ksettings_get(netdev->phydev, ks); + + return 0; +} + +static int dpaa2_mac_set_link_ksettings(struct net_device *netdev, + const struct ethtool_link_ksettings *ks) +{ + return phy_ethtool_ksettings_set(netdev->phydev, ks); +} + +static void dpaa2_mac_get_stats(struct net_device *netdev, + struct rtnl_link_stats64 *storage) +{ + struct dpaa2_mac_priv *priv = netdev_priv(netdev); + u64 tmp; + int err; + + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_EGR_MCAST_FRAME, + &storage->tx_packets); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_EGR_BCAST_FRAME, &tmp); + if (err) + goto error; + storage->tx_packets += tmp; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_EGR_UCAST_FRAME, &tmp); + if (err) + goto error; + storage->tx_packets += tmp; + + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_EGR_UNDERSIZED, &storage->tx_dropped); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_EGR_BYTE, &storage->tx_bytes); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_EGR_ERR_FRAME, &storage->tx_errors); + if (err) + goto error; + + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_ING_ALL_FRAME, &storage->rx_packets); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_ING_MCAST_FRAME, &storage->multicast); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_ING_FRAME_DISCARD, + &storage->rx_dropped); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_ING_ALIGN_ERR, &storage->rx_errors); + if (err) + goto error; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_ING_OVERSIZED, &tmp); + if (err) + goto error; + storage->rx_errors += tmp; + err = dpmac_get_counter(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle, + DPMAC_CNT_ING_BYTE, &storage->rx_bytes); + if (err) + goto error; + + return; +error: + netdev_err(netdev, "dpmac_get_counter err %d\n", err); +} + +static struct { + enum dpmac_counter id; + char name[ETH_GSTRING_LEN]; +} dpaa2_mac_counters[] = { + {DPMAC_CNT_ING_ALL_FRAME, "rx all frames"}, + {DPMAC_CNT_ING_GOOD_FRAME, "rx frames ok"}, + {DPMAC_CNT_ING_ERR_FRAME, "rx frame errors"}, + {DPMAC_CNT_ING_FRAME_DISCARD, "rx frame discards"}, + {DPMAC_CNT_ING_UCAST_FRAME, "rx u-cast"}, + {DPMAC_CNT_ING_BCAST_FRAME, "rx b-cast"}, + {DPMAC_CNT_ING_MCAST_FRAME, "rx m-cast"}, + {DPMAC_CNT_ING_FRAME_64, "rx 64 bytes"}, + {DPMAC_CNT_ING_FRAME_127, "rx 65-127 bytes"}, + {DPMAC_CNT_ING_FRAME_255, "rx 128-255 bytes"}, + {DPMAC_CNT_ING_FRAME_511, "rx 256-511 bytes"}, + {DPMAC_CNT_ING_FRAME_1023, "rx 512-1023 bytes"}, + {DPMAC_CNT_ING_FRAME_1518, "rx 1024-1518 bytes"}, + {DPMAC_CNT_ING_FRAME_1519_MAX, "rx 1519-max bytes"}, + {DPMAC_CNT_ING_FRAG, "rx frags"}, + {DPMAC_CNT_ING_JABBER, "rx jabber"}, + {DPMAC_CNT_ING_ALIGN_ERR, "rx align errors"}, + {DPMAC_CNT_ING_OVERSIZED, "rx oversized"}, + {DPMAC_CNT_ING_VALID_PAUSE_FRAME, "rx pause"}, + {DPMAC_CNT_ING_BYTE, "rx bytes"}, + {DPMAC_CNT_ENG_GOOD_FRAME, "tx frames ok"}, + {DPMAC_CNT_EGR_UCAST_FRAME, "tx u-cast"}, + {DPMAC_CNT_EGR_MCAST_FRAME, "tx m-cast"}, + {DPMAC_CNT_EGR_BCAST_FRAME, "tx b-cast"}, + {DPMAC_CNT_EGR_ERR_FRAME, "tx frame errors"}, + {DPMAC_CNT_EGR_UNDERSIZED, "tx undersized"}, + {DPMAC_CNT_EGR_VALID_PAUSE_FRAME, "tx b-pause"}, + {DPMAC_CNT_EGR_BYTE, "tx bytes"}, + +}; + +static void dpaa2_mac_get_strings(struct net_device *netdev, + u32 stringset, u8 *data) +{ + int i; + + switch (stringset) { + case ETH_SS_STATS: + for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) + memcpy(data + i * ETH_GSTRING_LEN, + dpaa2_mac_counters[i].name, + ETH_GSTRING_LEN); + break; + } +} + +static void dpaa2_mac_get_ethtool_stats(struct net_device *netdev, + struct ethtool_stats *stats, + u64 *data) +{ + struct dpaa2_mac_priv *priv = netdev_priv(netdev); + int i; + int err; + + for (i = 0; i < ARRAY_SIZE(dpaa2_mac_counters); i++) { + err = dpmac_get_counter(priv->mc_dev->mc_io, + 0, + priv->mc_dev->mc_handle, + dpaa2_mac_counters[i].id, &data[i]); + if (err) + netdev_err(netdev, "dpmac_get_counter[%s] err %d\n", + dpaa2_mac_counters[i].name, err); + } +} + +static int dpaa2_mac_get_sset_count(struct net_device *dev, int sset) +{ + switch (sset) { + case ETH_SS_STATS: + return ARRAY_SIZE(dpaa2_mac_counters); + default: + return -EOPNOTSUPP; + } +} + +static const struct net_device_ops dpaa2_mac_ndo_ops = { + .ndo_open = &dpaa2_mac_open, + .ndo_stop = &dpaa2_mac_stop, + .ndo_start_xmit = &dpaa2_mac_drop_frame, + .ndo_get_stats64 = &dpaa2_mac_get_stats, +}; + +static const struct ethtool_ops dpaa2_mac_ethtool_ops = { + .get_drvinfo = &dpaa2_mac_get_drvinfo, + .get_link_ksettings = &dpaa2_mac_get_link_ksettings, + .set_link_ksettings = &dpaa2_mac_set_link_ksettings, + .get_strings = &dpaa2_mac_get_strings, + .get_ethtool_stats = &dpaa2_mac_get_ethtool_stats, + .get_sset_count = &dpaa2_mac_get_sset_count, +}; +#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ + +static void configure_link(struct dpaa2_mac_priv *priv, + struct dpmac_link_cfg *cfg) +{ + struct phy_device *phydev = priv->netdev->phydev; + + if (unlikely(!phydev)) + return; + + phydev->speed = cfg->rate; + phydev->duplex = !!(cfg->options & DPMAC_LINK_OPT_HALF_DUPLEX); + + if (cfg->advertising != 0) { + phydev->advertising = 0; + link_mode_dpmac2phydev(cfg->advertising, &phydev->advertising); + } + + if (phydev->supported & SUPPORTED_Pause) { + if (cfg->options & DPMAC_LINK_OPT_PAUSE) + phydev->advertising |= ADVERTISED_Pause; + else + phydev->advertising &= ~ADVERTISED_Pause; + } + + if (phydev->supported & SUPPORTED_Asym_Pause) { + if (cfg->options & DPMAC_LINK_OPT_ASYM_PAUSE) + phydev->advertising |= ADVERTISED_Asym_Pause; + else + phydev->advertising &= ~ADVERTISED_Asym_Pause; + } + + if (cfg->options & DPMAC_LINK_OPT_AUTONEG) { + phydev->autoneg = AUTONEG_ENABLE; + phydev->advertising |= ADVERTISED_Autoneg; + } else { + phydev->autoneg = AUTONEG_DISABLE; + phydev->advertising &= ~ADVERTISED_Autoneg; + } + + phy_start_aneg(phydev); +} + +static irqreturn_t dpaa2_mac_irq_handler(int irq_num, void *arg) +{ + struct device *dev = (struct device *)arg; + struct fsl_mc_device *mc_dev = to_fsl_mc_device(dev); + struct dpaa2_mac_priv *priv = dev_get_drvdata(dev); + struct dpmac_link_cfg link_cfg = { 0 }; + u32 status; + int err; + + err = dpmac_get_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, + DPMAC_IRQ_INDEX, &status); + if (unlikely(err || !status)) + return IRQ_NONE; + + /* DPNI-initiated link configuration; 'ifconfig up' also calls this */ + if (status & DPMAC_IRQ_EVENT_LINK_CFG_REQ) { + if (cmp_dpmac_ver(priv, DPMAC_LINK_AUTONEG_VER_MAJOR, + DPMAC_LINK_AUTONEG_VER_MINOR) < 0) + err = dpmac_get_link_cfg(mc_dev->mc_io, 0, + mc_dev->mc_handle, &link_cfg); + else + err = dpmac_get_link_cfg_v2(mc_dev->mc_io, 0, + mc_dev->mc_handle, + &link_cfg); + if (unlikely(err)) + goto out; + + configure_link(priv, &link_cfg); + } + +out: + dpmac_clear_irq_status(mc_dev->mc_io, 0, mc_dev->mc_handle, + DPMAC_IRQ_INDEX, status); + + return IRQ_HANDLED; +} + +static int setup_irqs(struct fsl_mc_device *mc_dev) +{ + int err = 0; + struct fsl_mc_device_irq *irq; + + err = fsl_mc_allocate_irqs(mc_dev); + if (err) { + dev_err(&mc_dev->dev, "fsl_mc_allocate_irqs err %d\n", err); + return err; + } + + irq = mc_dev->irqs[0]; + err = devm_request_threaded_irq(&mc_dev->dev, irq->msi_desc->irq, + NULL, &dpaa2_mac_irq_handler, + IRQF_NO_SUSPEND | IRQF_ONESHOT, + dev_name(&mc_dev->dev), &mc_dev->dev); + if (err) { + dev_err(&mc_dev->dev, "devm_request_threaded_irq err %d\n", + err); + goto free_irq; + } + + err = dpmac_set_irq_mask(mc_dev->mc_io, 0, mc_dev->mc_handle, + DPMAC_IRQ_INDEX, DPMAC_IRQ_EVENT_LINK_CFG_REQ); + if (err) { + dev_err(&mc_dev->dev, "dpmac_set_irq_mask err %d\n", err); + goto free_irq; + } + err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, + DPMAC_IRQ_INDEX, 1); + if (err) { + dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err); + goto free_irq; + } + + return 0; + +free_irq: + fsl_mc_free_irqs(mc_dev); + + return err; +} + +static void teardown_irqs(struct fsl_mc_device *mc_dev) +{ + int err; + + err = dpmac_set_irq_enable(mc_dev->mc_io, 0, mc_dev->mc_handle, + DPMAC_IRQ_INDEX, 0); + if (err) + dev_err(&mc_dev->dev, "dpmac_set_irq_enable err %d\n", err); + + fsl_mc_free_irqs(mc_dev); +} + +static struct device_node *find_dpmac_node(struct device *dev, u16 dpmac_id) +{ + struct device_node *dpmacs, *dpmac = NULL; + struct device_node *mc_node = dev->of_node; + u32 id; + int err; + + dpmacs = of_find_node_by_name(mc_node, "dpmacs"); + if (!dpmacs) { + dev_err(dev, "No dpmacs subnode in device-tree\n"); + return NULL; + } + + while ((dpmac = of_get_next_child(dpmacs, dpmac))) { + err = of_property_read_u32(dpmac, "reg", &id); + if (err) + continue; + if (id == dpmac_id) + return dpmac; + } + + return NULL; +} + +static int dpaa2_mac_probe(struct fsl_mc_device *mc_dev) +{ + struct device *dev; + struct dpaa2_mac_priv *priv = NULL; + struct device_node *phy_node, *dpmac_node; + struct net_device *netdev; + int if_mode; + int err = 0; + + dev = &mc_dev->dev; + + /* prepare a net_dev structure to make the phy lib API happy */ + netdev = alloc_etherdev(sizeof(*priv)); + if (!netdev) { + dev_err(dev, "alloc_etherdev error\n"); + err = -ENOMEM; + goto err_exit; + } + priv = netdev_priv(netdev); + priv->mc_dev = mc_dev; + priv->netdev = netdev; + + SET_NETDEV_DEV(netdev, dev); + +#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS + snprintf(netdev->name, IFNAMSIZ, "mac%d", mc_dev->obj_desc.id); +#endif + + dev_set_drvdata(dev, priv); + + /* We may need to issue MC commands while in atomic context */ + err = fsl_mc_portal_allocate(mc_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL, + &mc_dev->mc_io); + if (err || !mc_dev->mc_io) { + dev_dbg(dev, "fsl_mc_portal_allocate error: %d\n", err); + err = -EPROBE_DEFER; + goto err_free_netdev; + } + + err = dpmac_open(mc_dev->mc_io, 0, mc_dev->obj_desc.id, + &mc_dev->mc_handle); + if (err || !mc_dev->mc_handle) { + dev_err(dev, "dpmac_open error: %d\n", err); + err = -ENODEV; + goto err_free_mcp; + } + + err = dpmac_get_api_version(mc_dev->mc_io, 0, &priv->dpmac_ver_major, + &priv->dpmac_ver_minor); + if (err) { + dev_err(dev, "dpmac_get_api_version failed\n"); + goto err_version; + } + + if (cmp_dpmac_ver(priv, DPMAC_VER_MAJOR, DPMAC_VER_MINOR) < 0) { + dev_err(dev, "DPMAC version %u.%u lower than supported %u.%u\n", + priv->dpmac_ver_major, priv->dpmac_ver_minor, + DPMAC_VER_MAJOR, DPMAC_VER_MINOR); + err = -ENOTSUPP; + goto err_version; + } + + err = dpmac_get_attributes(mc_dev->mc_io, 0, + mc_dev->mc_handle, &priv->attr); + if (err) { + dev_err(dev, "dpmac_get_attributes err %d\n", err); + err = -EINVAL; + goto err_close; + } + + /* Look up the DPMAC node in the device-tree. */ + dpmac_node = find_dpmac_node(dev, priv->attr.id); + if (!dpmac_node) { + dev_err(dev, "No dpmac@%d subnode found.\n", priv->attr.id); + err = -ENODEV; + goto err_close; + } + + err = setup_irqs(mc_dev); + if (err) { + err = -EFAULT; + goto err_close; + } + +#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS + /* OPTIONAL, register netdev just to make it visible to the user */ + netdev->netdev_ops = &dpaa2_mac_ndo_ops; + netdev->ethtool_ops = &dpaa2_mac_ethtool_ops; + + /* phy starts up enabled so netdev should be up too */ + netdev->flags |= IFF_UP; + + err = register_netdev(priv->netdev); + if (err < 0) { + dev_err(dev, "register_netdev error %d\n", err); + err = -ENODEV; + goto err_free_irq; + } +#endif /* CONFIG_FSL_DPAA2_MAC_NETDEVS */ + + /* get the interface mode from the dpmac of node or from the MC attributes */ + if_mode = of_get_phy_mode(dpmac_node); + if (if_mode >= 0) { + dev_dbg(dev, "\tusing if mode %s for eth_if %d\n", + phy_modes(if_mode), priv->attr.eth_if); + goto link_type; + } + + if (priv->attr.eth_if < ARRAY_SIZE(dpaa2_mac_iface_mode)) { + if_mode = dpaa2_mac_iface_mode[priv->attr.eth_if]; + dev_dbg(dev, "\tusing if mode %s for eth_if %d\n", + phy_modes(if_mode), priv->attr.eth_if); + } else { + dev_err(dev, "Unexpected interface mode %d\n", + priv->attr.eth_if); + err = -EINVAL; + goto err_no_if_mode; + } + +link_type: + /* probe the PHY as fixed-link if the DPMAC attribute indicates so */ + if (priv->attr.link_type == DPMAC_LINK_TYPE_FIXED) + goto probe_fixed_link; + + /* or if there's no phy-handle defined in the device tree */ + phy_node = of_parse_phandle(dpmac_node, "phy-handle", 0); + if (!phy_node) { + goto probe_fixed_link; + } + + /* try to connect to the PHY */ + netdev->phydev = of_phy_connect(netdev, phy_node, + &dpaa2_mac_link_changed, 0, if_mode); + if (!netdev->phydev) { + /* No need for dev_err(); the kernel's loud enough as it is. */ + dev_dbg(dev, "Can't of_phy_connect() now.\n"); + /* We might be waiting for the MDIO MUX to probe, so defer + * our own probing. + */ + err = -EPROBE_DEFER; + goto err_defer; + } + dev_info(dev, "Connected to %s PHY.\n", phy_modes(if_mode)); + +probe_fixed_link: + if (!netdev->phydev) { + struct fixed_phy_status status = { + .link = 1, + /* fixed-phys don't support 10Gbps speed for now */ + .speed = 1000, + .duplex = 1, + }; + + /* try to register a fixed link phy */ + netdev->phydev = fixed_phy_register(PHY_POLL, &status, -1, + NULL); + if (!netdev->phydev || IS_ERR(netdev->phydev)) { + dev_err(dev, "error trying to register fixed PHY\n"); + /* So we don't crash unregister_netdev() later on */ + netdev->phydev = NULL; + err = -EFAULT; + goto err_no_phy; + } + + err = phy_connect_direct(netdev, netdev->phydev, + &dpaa2_mac_link_changed, if_mode); + if (err) { + dev_err(dev, "error trying to connect to PHY\n"); + goto err_no_phy; + } + + dev_info(dev, "Registered fixed PHY.\n"); + } + + dpaa2_mac_open(netdev); + + return 0; + +err_no_if_mode: +err_defer: +err_no_phy: +#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS + unregister_netdev(netdev); +err_free_irq: +#endif + teardown_irqs(mc_dev); +err_version: +err_close: + dpmac_close(mc_dev->mc_io, 0, mc_dev->mc_handle); +err_free_mcp: + fsl_mc_portal_free(mc_dev->mc_io); +err_free_netdev: + free_netdev(netdev); +err_exit: + return err; +} + +static int dpaa2_mac_remove(struct fsl_mc_device *mc_dev) +{ + struct device *dev = &mc_dev->dev; + struct dpaa2_mac_priv *priv = dev_get_drvdata(dev); + struct net_device *netdev = priv->netdev; + + dpaa2_mac_stop(netdev); + + if (phy_is_pseudo_fixed_link(netdev->phydev)) + fixed_phy_unregister(netdev->phydev); + else + phy_disconnect(netdev->phydev); + netdev->phydev = NULL; + +#ifdef CONFIG_FSL_DPAA2_MAC_NETDEVS + unregister_netdev(priv->netdev); +#endif + teardown_irqs(priv->mc_dev); + dpmac_close(priv->mc_dev->mc_io, 0, priv->mc_dev->mc_handle); + fsl_mc_portal_free(priv->mc_dev->mc_io); + free_netdev(priv->netdev); + + dev_set_drvdata(dev, NULL); + + return 0; +} + +static const struct fsl_mc_device_id dpaa2_mac_match_id_table[] = { + { + .vendor = FSL_MC_VENDOR_FREESCALE, + .obj_type = "dpmac", + }, + { .vendor = 0x0 } +}; +MODULE_DEVICE_TABLE(fslmc, dpaa2_mac_match_id_table); + +static struct fsl_mc_driver dpaa2_mac_drv = { + .driver = { + .name = KBUILD_MODNAME, + .owner = THIS_MODULE, + }, + .probe = dpaa2_mac_probe, + .remove = dpaa2_mac_remove, + .match_id_table = dpaa2_mac_match_id_table, +}; + +module_fsl_mc_driver(dpaa2_mac_drv); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DPAA2 PHY proxy interface driver");