Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934791AbbGVPmg (ORCPT ); Wed, 22 Jul 2015 11:42:36 -0400 Received: from mail-bn1bon0138.outbound.protection.outlook.com ([157.56.111.138]:48363 "EHLO na01-bn1-obe.outbound.protection.outlook.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S934577AbbGVPmU (ORCPT ); Wed, 22 Jul 2015 11:42:20 -0400 Authentication-Results: spf=fail (sender IP is 192.88.168.50) smtp.mailfrom=freescale.com; freescale.mail.onmicrosoft.com; dkim=none (message not signed) header.d=none; From: To: CC: , , , , , , , Igal Liberman Subject: [v3, 6/9] fsl/fman: Add FMan MAC support Date: Wed, 22 Jul 2015 14:22:29 +0300 Message-ID: <1437564149-16626-1-git-send-email-igal.liberman@freescale.com> X-Mailer: git-send-email 1.7.9.5 Reply-To: X-EOPAttributedMessage: 0 X-Microsoft-Exchange-Diagnostics: 1;BL2FFO11OLC008;1:QYouBK5NfLVQc2oM3safly4MeV5GsTTslkmM9aFlTT1VXjSJPaIfDTNfJH0usvluok3601e6R/uuZw/VfwkKZhOCDx7DVJGuiuj5pLbNK7AMYYBG4PTNLuOekX0GrryLkKOIzgFJhyf6OO9tt2/W9Tmkn8CbiL0CLwM3pttbKdH5ds83zsOhqbLgX8MeviWqX2U5fEtXoEbEf0tqEtemCdQ3f4W3LPkoOeM6zp3zedZKdFCLIBkrqZEBN4WW4sTQnvbp75gOH6eqITBLohbvBJQCWDQ27Lv6/2E7J2TtFtrsjvzJMH08nr5Fc14pEs8LsHW5MtQtYiw4BLOu9ODwkA== X-Forefront-Antispam-Report: CIP:192.88.168.50;CTRY:US;IPV:NLI;EFV:NLI;SFV:NSPM;SFS:(10019020)(6009001)(2980300002)(339900001)(199003)(189002)(77096005)(48376002)(43066003)(62966003)(77156002)(50986999)(50466002)(53806999)(106466001)(105606002)(50226001)(19580395003)(19580405001)(6806004)(46102003)(5001960100002)(87936001)(33646002)(229853001)(47776003)(2351001)(5003940100001)(85426001)(86362001)(575784001)(86152002)(104016003)(189998001)(110136002)(107886002)(36756003)(4001430100001)(579004)(559001)(357404004);DIR:OUT;SFP:1102;SCL:1;SRVR:DM2PR03MB383;H:tx30smr01.am.freescale.net;FPR:;SPF:Fail;MLV:sfv;A:1;MX:1;LANG:en; MIME-Version: 1.0 Content-Type: text/plain X-Microsoft-Exchange-Diagnostics: 1;DM2PR03MB383;2:jPh8twT/Jen6NQSvwIXucPM/Li42XXXt7bv8YfqhjdYOLo2YJ0xQ68rFe3v8X3R+umKYTJfM9ih7ixrWJfak05C5CvbmptxYWF7F2gt7ttSjA1Avfnfci9FkD1bgDNkaZnFSlJyTtpH0coXVvN0X2hZqzszWRxFWinBR3IavX0Y=;3:uQVkbsS4ioZGSw7a6lkYSR7/J70eiWycGTwriTlSjCYAy9sQd4dh1WE4D001V1+RsndD9dN6M43GcoT3q3oxREccKRrB5YjDzOBM5fJZoElaqyVF8fVt901qhhHu/Y2gNLbNbPW6FnO8vQtM/LBP5JNt7OpBaUTLqX8XXmRVBlNYSXaYhIJMgp9EVTMpTx4Ry3PMUlNL2TNRgXXo+jQAAdAZjeWFvsnlXqWeiOhrTYg=;25:j5/X9sSvagFUv2ToRWF7KyjlRDN2rPbf7fSdwE3PStaesnQN/TLBW15SzEzRiQ7cUVDthJwUoS994sjxpTWbKyhzHz3ttWGTqO0yWZqUQ35EpyjXlfNJimf09oXjXZI/GapyQy38MlJD0F6hAONxCch1i3vc4F+hzm141x27FL0JzHKvdoIyfnAeNJ7zLjkZl/0aRTTrhjqyefW96OcEydikXBJsoOp/leIilakVVmj/rPH0bmlLb6YjIiRCjMiK2Kyy8ym0Rn8tAA0pN0pbQg== X-Microsoft-Antispam: UriScan:;BCL:0;PCL:0;RULEID:;SRVR:DM2PR03MB383; X-Microsoft-Exchange-Diagnostics: 1;DM2PR03MB383;20:RgZnAv1XpVds+7+n2P53w/dJcaHNaeJtI+El6jThI1NqjsOQGNUJH4MkaM7I8CtyhTvLVexMR9D6gE4SZslCMSjdt/9wT8fB392fsnh8wVMukaRdBRxd9AY7tmU5TDDDCTWl0aLSZd3tTn92BsS9sxD68U/B97xO240RrZqEwIS/s5v5E3Zr6cIogAfYRuFvhZ5+BURqQnOIhccp06igoSAjNKViSlVtB+BP7vao6XXtNGDeqns0G71xaSMq6/FlX6MEpRL9tS2bzFVBqHCfQlYmfcUa/EGCAs4L8hUb3Q3QFNDO4m3ZhnANS7mfHcAh0SxT+A5XfypU4ddujs1IPEP+ruw7+yQe0eAzYaHrGdM=;4:9QIpY/iR7cRdxtC5JOs/stq9zeb6k8+pjHq6n43tgZCDWUpSuFdmb+HmTzz1dRivWuh0L/0HBZGiAYpUff/nuVPLlZ7HFEmO0uuxj7fz9Rd0AfMcDkEDw1NFl4+e2lf9KmutPZo9T6m3wm1r1IUXhYHGtszlCLvCx0gfykNwj+So8w3pIdiNKhINlC05ZXlg6UjsWljRaY/M3VGwJ4RGDzR7aPyEOeDXFMxqBiXIzbBmbW1DlflSflni4ZGgGMF75QyB9T+K2aDacuIiZ7Qo0iAZE0I3kBUvuoT0t/j2FbM= DM2PR03MB383: X-MS-Exchange-Organization-RulesExecuted X-Microsoft-Antispam-PRVS: X-Exchange-Antispam-Report-Test: UriScan:; X-Exchange-Antispam-Report-CFA-Test: BCL:0;PCL:0;RULEID:(601004)(5005006)(3002001);SRVR:DM2PR03MB383;BCL:0;PCL:0;RULEID:;SRVR:DM2PR03MB383; X-Forefront-PRVS: 0645BEB7AA X-Microsoft-Exchange-Diagnostics: =?us-ascii?Q?1;DM2PR03MB383;23:HVHPAxSAzSTW3PwdEfhokc3BryUN/KLxhGy4aAHCSQ?= =?us-ascii?Q?Th5NZ1ge6bXr/GXSdQEZYk3MjU9Eha/KXQfHZb8cS27xi/aVbJH149Dd6d+G?= =?us-ascii?Q?mJ9gForzQnKKgRfgw44J2z5+Ie8sv0fWeo6RI4OPkZFIAX4p+KObuK5otDSc?= =?us-ascii?Q?fAUUIn+lLtzgO73dC7xx/rcE3OxJwWkylEb/2zY4zbLlZuxO3sIn3Al5vJjb?= =?us-ascii?Q?E+sEySxFS2Avmc6zDmmqVi8SBgdFT4vzeZrzUvs5TkydbgLi4QwNxfF3kNFZ?= =?us-ascii?Q?SoCTS+wJSbiQBpwqD31jCSlmDU8JEx1HH+OmMgEq2h4KQJLm+uRSD/OqmMlL?= =?us-ascii?Q?XxkhIljj/g/ZTUuyuqaVCUnHE2XTRld3jGckrbi3MxdmF/HxoCqJioECknkx?= =?us-ascii?Q?yHxL320E0Yvyu7327seL2zhZQKAipVAPRKJDOBvaXVXMmiuy0vj8ip3vcx4T?= =?us-ascii?Q?/uz5GlHsYuCU5sOdUkdbCNUZrU2soiF78aIQdJkbV0Ew0Ox/9Xv2b0zkZEEf?= =?us-ascii?Q?FY6LLY2BvlOlK5ne/IDjIW+9fWwQ8ki3TE1GccNsJLYcUFtw2ZlPUVPnOw6h?= =?us-ascii?Q?xE/yZSmOM/kcT3L6X3F7idBRehNRxXQT/JcoODxm1601lLyHDFh85a2XV9Ec?= =?us-ascii?Q?+K5fwdkQKfMMDniVfFQ9AWbdYseOnJ8EdoWsIttZVnB8SWTa/1PMBt5IST72?= =?us-ascii?Q?3cKm7rZjqnAqo4/oMam2rjRtYaV8xHhREx/PBPtZBTcGcgUwAJp36l/INvlN?= =?us-ascii?Q?6E496/MlCx2Jx5a5X+9D2roqu+fxKVeiM9+cUkM2ZBSWZqQZkWgciuc1E49i?= =?us-ascii?Q?QmRvxIDNskElwodkSBmd73dQM956O3B6jQtZlN/e2yksGCB9HD20aBrw0zxj?= =?us-ascii?Q?C1qRlvE1R8hQckZfcDKPFWg4R6PHuxaeNbKuIfRDG+9SsmcoM+N97QpSgsMn?= =?us-ascii?Q?PP7PRTmVHBQk53wCZdrS2ryq6Q0et07U0ohqh8smS4z3g/twe1vTyCufm3m8?= =?us-ascii?Q?hlKiJK4MVw/IgJWmY8vn3lfw/wW79D/MoH76irymZ5KasBtJJpYJlVVY/E80?= =?us-ascii?Q?uqmLQ=3D?= X-Microsoft-Exchange-Diagnostics: 1;DM2PR03MB383;5:3L4XZDS3PJsnaJj1PEvKV4HFq694HKR84+UJJ33P+NcRmakTAZkCMnN8kn67a/lHkK69rr9VpGTEK/xonOG3sZMi5oXrKBJBUH/WNMSe0rMsbo6s0ZA3iy5bDy7R62ebTDo97cVY4HKk85bCPMgkAw==;24:6SV1Fg4C0xySaRIiu18Wa5ylS8XIN07qy+Y8m4dLir2cmUC7uEDByShp57Juardn3pKRgHt1PTLmbc0rjUIBHQqrfHkn5vhin6G2RwayVNM=;20:3wQ2UYn3u6O/S/Ty5B0kytI5grXxbLczkML2nRsIHwsT5hUQ5Y5eQsBCWHTmXc5BF2rW9yg4trLPNZiNv8qleA== X-OriginatorOrg: freescale.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Jul 2015 15:27:09.2155 (UTC) X-MS-Exchange-CrossTenant-Id: 710a03f5-10f6-4d38-9ff4-a80b81da590d X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=710a03f5-10f6-4d38-9ff4-a80b81da590d;Ip=[192.88.168.50];Helo=[tx30smr01.am.freescale.net] X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM2PR03MB383 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 103618 Lines: 3482 From: Igal Liberman Add Frame Manger MAC Driver support. This patch adds The FMan MAC configuration, initialization and runtime control routines. This patch contains support for these types of MACs: tGEC, dTSEC and mEMAC Signed-off-by: Igal Liberman --- drivers/net/ethernet/freescale/fman/fm.c | 41 + drivers/net/ethernet/freescale/fman/fm.h | 3 + drivers/net/ethernet/freescale/fman/fm_common.h | 29 + .../ethernet/freescale/fman/inc/crc_mac_addr_ext.h | 314 ++++++ drivers/net/ethernet/freescale/fman/mac/Makefile | 4 +- drivers/net/ethernet/freescale/fman/mac/fm_dtsec.c | 1012 ++++++++++++++++++++ drivers/net/ethernet/freescale/fman/mac/fm_dtsec.h | 207 ++++ drivers/net/ethernet/freescale/fman/mac/fm_mac.h | 259 +++++ drivers/net/ethernet/freescale/fman/mac/fm_memac.c | 673 +++++++++++++ drivers/net/ethernet/freescale/fman/mac/fm_memac.h | 115 +++ drivers/net/ethernet/freescale/fman/mac/fm_tgec.c | 552 +++++++++++ drivers/net/ethernet/freescale/fman/mac/fm_tgec.h | 124 +++ 12 files changed, 3332 insertions(+), 1 deletion(-) create mode 100644 drivers/net/ethernet/freescale/fman/inc/crc_mac_addr_ext.h create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_dtsec.c create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_dtsec.h create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_mac.h create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_memac.c create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_memac.h create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_tgec.c create mode 100644 drivers/net/ethernet/freescale/fman/mac/fm_tgec.h diff --git a/drivers/net/ethernet/freescale/fman/fm.c b/drivers/net/ethernet/freescale/fman/fm.c index 7e5fa53..450ee6b 100644 --- a/drivers/net/ethernet/freescale/fman/fm.c +++ b/drivers/net/ethernet/freescale/fman/fm.c @@ -587,6 +587,47 @@ u8 fm_get_id(struct fm_t *fm) return fm->fm_state->fm_id; } +int fm_reset_mac(struct fm_t *fm, u8 mac_id) +{ + int err; + struct fman_fpm_regs __iomem *fpm_rg = fm->fpm_regs; + + if (fm->fm_state->rev_info.major_rev >= 6) { + pr_warn("FMan MAC reset!\n"); + return -EINVAL; + } + if (!fm->base_addr) { + pr_warn("'base_address' is required!\n"); + return -EINVAL; + } + err = fman_reset_mac(fpm_rg, mac_id); + + if (err == -EINVAL) { + pr_warn("Illegal MAC Id\n"); + return -EINVAL; + } else if (err == EINVAL) { + return -EINVAL; + } + return 0; +} + +int fm_set_mac_max_frame(struct fm_t *fm, enum fm_mac_type type, + u8 mac_id, u16 mtu) +{ + /* if port is already initialized, check that MaxFrameLength is smaller + * or equal to the port's max + */ + if ((!fm->fm_state->port_mfl[mac_id]) || + (fm->fm_state->port_mfl[mac_id] && + (mtu <= fm->fm_state->port_mfl[mac_id]))) { + fm->fm_state->mac_mfl[mac_id] = mtu; + } else { + pr_warn("MAC max_frame_length is larger than Port max_frame_length\n"); + return -EINVAL; + } + return 0; +} + u16 fm_get_clock_freq(struct fm_t *fm) { return fm->fm_state->fm_clk_freq; diff --git a/drivers/net/ethernet/freescale/fman/fm.h b/drivers/net/ethernet/freescale/fman/fm.h index d7eca90..c205357 100644 --- a/drivers/net/ethernet/freescale/fman/fm.h +++ b/drivers/net/ethernet/freescale/fman/fm.h @@ -164,6 +164,7 @@ struct fm_iram_regs_t { struct fm_state_struct_t { u8 fm_id; + enum fm_port_type ports_types[FM_MAX_NUM_OF_HW_PORT_IDS]; u16 fm_clk_freq; struct fm_revision_info_t rev_info; bool enabled_time_stamp; @@ -183,6 +184,8 @@ struct fm_state_struct_t { u32 extra_fifo_pool_size; u8 extra_tasks_pool_size; u8 extra_open_dmas_pool_size; + u16 port_mfl[FM_MAX_NUM_OF_MACS]; + u16 mac_mfl[FM_MAX_NUM_OF_MACS]; }; struct fm_intg_t { diff --git a/drivers/net/ethernet/freescale/fman/fm_common.h b/drivers/net/ethernet/freescale/fman/fm_common.h index 1cde270..abe89a7 100644 --- a/drivers/net/ethernet/freescale/fman/fm_common.h +++ b/drivers/net/ethernet/freescale/fman/fm_common.h @@ -86,6 +86,26 @@ enum fm_inter_module_event { #define FM_LIODN_OFFSET_MASK 0x3FF +/* Port Id defines */ +#define BASE_RX_PORTID 0x08 +#define BASE_TX_PORTID 0x28 + +static inline u8 hw_port_id_to_sw_port_id(u8 major, u8 hw_port_id) +{ + u8 sw_port_id = 0; + + if (hw_port_id >= BASE_TX_PORTID) { + sw_port_id = hw_port_id - BASE_TX_PORTID; + } else if (hw_port_id >= BASE_RX_PORTID) { + sw_port_id = hw_port_id - BASE_RX_PORTID; + } else { + sw_port_id = DUMMY_PORT_ID; + BUG_ON(false); + } + + return sw_port_id; +} + #define BMI_MAX_FIFO_SIZE (FM_MURAM_SIZE) #define BMI_FIFO_UNITS 0x100 @@ -94,6 +114,12 @@ struct fm_intr_src_t { void *src_handle; }; +/* enum for defining MAC types */ +enum fm_mac_type { + FM_MAC_10G = 0, /* 10G MAC */ + FM_MAC_1G /* 1G MAC */ +}; + void fm_register_intr(struct fm_t *fm, enum fm_event_modules mod, u8 mod_id, enum fm_intr_type intr_type, void (*f_isr)(void *h_src_arg), void *h_src_arg); @@ -111,4 +137,7 @@ u8 fm_get_id(struct fm_t *fm); u32 fm_get_bmi_max_fifo_size(struct fm_t *fm); +int fm_set_mac_max_frame(struct fm_t *fm, enum fm_mac_type type, u8 mac_id, + u16 mtu); + #endif /* __FM_COMMON_H */ diff --git a/drivers/net/ethernet/freescale/fman/inc/crc_mac_addr_ext.h b/drivers/net/ethernet/freescale/fman/inc/crc_mac_addr_ext.h new file mode 100644 index 0000000..190f739 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/inc/crc_mac_addr_ext.h @@ -0,0 +1,314 @@ +/* + * Copyright 2008-2015 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. + */ + +/* Define a macro that calculate the crc value of an Ethernet MAC address + * (48 bitd address) + */ + +#ifndef __crc_mac_addr_ext_h +#define __crc_mac_addr_ext_h + +#include + +static u32 crc_table[256] = { + 0x00000000, + 0x77073096, + 0xee0e612c, + 0x990951ba, + 0x076dc419, + 0x706af48f, + 0xe963a535, + 0x9e6495a3, + 0x0edb8832, + 0x79dcb8a4, + 0xe0d5e91e, + 0x97d2d988, + 0x09b64c2b, + 0x7eb17cbd, + 0xe7b82d07, + 0x90bf1d91, + 0x1db71064, + 0x6ab020f2, + 0xf3b97148, + 0x84be41de, + 0x1adad47d, + 0x6ddde4eb, + 0xf4d4b551, + 0x83d385c7, + 0x136c9856, + 0x646ba8c0, + 0xfd62f97a, + 0x8a65c9ec, + 0x14015c4f, + 0x63066cd9, + 0xfa0f3d63, + 0x8d080df5, + 0x3b6e20c8, + 0x4c69105e, + 0xd56041e4, + 0xa2677172, + 0x3c03e4d1, + 0x4b04d447, + 0xd20d85fd, + 0xa50ab56b, + 0x35b5a8fa, + 0x42b2986c, + 0xdbbbc9d6, + 0xacbcf940, + 0x32d86ce3, + 0x45df5c75, + 0xdcd60dcf, + 0xabd13d59, + 0x26d930ac, + 0x51de003a, + 0xc8d75180, + 0xbfd06116, + 0x21b4f4b5, + 0x56b3c423, + 0xcfba9599, + 0xb8bda50f, + 0x2802b89e, + 0x5f058808, + 0xc60cd9b2, + 0xb10be924, + 0x2f6f7c87, + 0x58684c11, + 0xc1611dab, + 0xb6662d3d, + 0x76dc4190, + 0x01db7106, + 0x98d220bc, + 0xefd5102a, + 0x71b18589, + 0x06b6b51f, + 0x9fbfe4a5, + 0xe8b8d433, + 0x7807c9a2, + 0x0f00f934, + 0x9609a88e, + 0xe10e9818, + 0x7f6a0dbb, + 0x086d3d2d, + 0x91646c97, + 0xe6635c01, + 0x6b6b51f4, + 0x1c6c6162, + 0x856530d8, + 0xf262004e, + 0x6c0695ed, + 0x1b01a57b, + 0x8208f4c1, + 0xf50fc457, + 0x65b0d9c6, + 0x12b7e950, + 0x8bbeb8ea, + 0xfcb9887c, + 0x62dd1ddf, + 0x15da2d49, + 0x8cd37cf3, + 0xfbd44c65, + 0x4db26158, + 0x3ab551ce, + 0xa3bc0074, + 0xd4bb30e2, + 0x4adfa541, + 0x3dd895d7, + 0xa4d1c46d, + 0xd3d6f4fb, + 0x4369e96a, + 0x346ed9fc, + 0xad678846, + 0xda60b8d0, + 0x44042d73, + 0x33031de5, + 0xaa0a4c5f, + 0xdd0d7cc9, + 0x5005713c, + 0x270241aa, + 0xbe0b1010, + 0xc90c2086, + 0x5768b525, + 0x206f85b3, + 0xb966d409, + 0xce61e49f, + 0x5edef90e, + 0x29d9c998, + 0xb0d09822, + 0xc7d7a8b4, + 0x59b33d17, + 0x2eb40d81, + 0xb7bd5c3b, + 0xc0ba6cad, + 0xedb88320, + 0x9abfb3b6, + 0x03b6e20c, + 0x74b1d29a, + 0xead54739, + 0x9dd277af, + 0x04db2615, + 0x73dc1683, + 0xe3630b12, + 0x94643b84, + 0x0d6d6a3e, + 0x7a6a5aa8, + 0xe40ecf0b, + 0x9309ff9d, + 0x0a00ae27, + 0x7d079eb1, + 0xf00f9344, + 0x8708a3d2, + 0x1e01f268, + 0x6906c2fe, + 0xf762575d, + 0x806567cb, + 0x196c3671, + 0x6e6b06e7, + 0xfed41b76, + 0x89d32be0, + 0x10da7a5a, + 0x67dd4acc, + 0xf9b9df6f, + 0x8ebeeff9, + 0x17b7be43, + 0x60b08ed5, + 0xd6d6a3e8, + 0xa1d1937e, + 0x38d8c2c4, + 0x4fdff252, + 0xd1bb67f1, + 0xa6bc5767, + 0x3fb506dd, + 0x48b2364b, + 0xd80d2bda, + 0xaf0a1b4c, + 0x36034af6, + 0x41047a60, + 0xdf60efc3, + 0xa867df55, + 0x316e8eef, + 0x4669be79, + 0xcb61b38c, + 0xbc66831a, + 0x256fd2a0, + 0x5268e236, + 0xcc0c7795, + 0xbb0b4703, + 0x220216b9, + 0x5505262f, + 0xc5ba3bbe, + 0xb2bd0b28, + 0x2bb45a92, + 0x5cb36a04, + 0xc2d7ffa7, + 0xb5d0cf31, + 0x2cd99e8b, + 0x5bdeae1d, + 0x9b64c2b0, + 0xec63f226, + 0x756aa39c, + 0x026d930a, + 0x9c0906a9, + 0xeb0e363f, + 0x72076785, + 0x05005713, + 0x95bf4a82, + 0xe2b87a14, + 0x7bb12bae, + 0x0cb61b38, + 0x92d28e9b, + 0xe5d5be0d, + 0x7cdcefb7, + 0x0bdbdf21, + 0x86d3d2d4, + 0xf1d4e242, + 0x68ddb3f8, + 0x1fda836e, + 0x81be16cd, + 0xf6b9265b, + 0x6fb077e1, + 0x18b74777, + 0x88085ae6, + 0xff0f6a70, + 0x66063bca, + 0x11010b5c, + 0x8f659eff, + 0xf862ae69, + 0x616bffd3, + 0x166ccf45, + 0xa00ae278, + 0xd70dd2ee, + 0x4e048354, + 0x3903b3c2, + 0xa7672661, + 0xd06016f7, + 0x4969474d, + 0x3e6e77db, + 0xaed16a4a, + 0xd9d65adc, + 0x40df0b66, + 0x37d83bf0, + 0xa9bcae53, + 0xdebb9ec5, + 0x47b2cf7f, + 0x30b5ffe9, + 0xbdbdf21c, + 0xcabac28a, + 0x53b39330, + 0x24b4a3a6, + 0xbad03605, + 0xcdd70693, + 0x54de5729, + 0x23d967bf, + 0xb3667a2e, + 0xc4614ab8, + 0x5d681b02, + 0x2a6f2b94, + 0xb40bbe37, + 0xc30c8ea1, + 0x5a05df1b, + 0x2d02ef8d +}; + +/* CRC calculation */ +#define GET_MAC_ADDR_CRC(addr, crc) \ +{ \ + u32 i; \ + u8 data; \ + crc = 0xffffffff; \ + for (i = 0; i < 6; i++) { \ + data = (u8)(addr >> ((5 - i) * 8)); \ + crc = crc ^ data; \ + crc = crc_table[crc & 0xff] ^ (crc >> 8); \ + } \ +} \ + +#endif /* __crc_mac_addr_ext_h */ diff --git a/drivers/net/ethernet/freescale/fman/mac/Makefile b/drivers/net/ethernet/freescale/fman/mac/Makefile index ce03e25..26e35e1 100644 --- a/drivers/net/ethernet/freescale/fman/mac/Makefile +++ b/drivers/net/ethernet/freescale/fman/mac/Makefile @@ -1,5 +1,7 @@ obj-y += fsl_fman_mac.o fsl_fman_mac-objs := fman_dtsec.o fman_dtsec_mii_acc.o \ + fm_dtsec.o \ fman_memac.o fman_memac_mii_acc.o \ - fman_tgec.o + fm_memac.o \ + fman_tgec.o fm_tgec.o diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_dtsec.c b/drivers/net/ethernet/freescale/fman/mac/fm_dtsec.c new file mode 100644 index 0000000..941a264 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_dtsec.c @@ -0,0 +1,1012 @@ +/* + * Copyright 2008-2015 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "crc_mac_addr_ext.h" + +#include "fm_common.h" +#include "fm_dtsec.h" +#include "fsl_fman_dtsec.h" +#include "fsl_fman_dtsec_mii_acc.h" + +#include +#include +#include + +static int check_init_parameters(struct dtsec_t *dtsec) +{ + if (ENET_SPEED_FROM_MODE(dtsec->enet_mode) >= ENET_SPEED_10000) { + pr_err("1G MAC driver supports 1G or lower speeds\n"); + return -EINVAL; + } + if (dtsec->addr == 0) { + pr_err("Ethernet MAC Must have a valid MAC Address\n"); + return -EINVAL; + } + if ((ENET_SPEED_FROM_MODE(dtsec->enet_mode) >= ENET_SPEED_1000) && + dtsec->dtsec_drv_param->halfdup_on) { + pr_err("Ethernet MAC 1G can't work in half duplex\n"); + return -EINVAL; + } + + /* FM_RX_PREAM_4_ERRATA_DTSEC_A001 Errata workaround */ + if (dtsec->dtsec_drv_param->rx_preamble) { + pr_err("preamble_rx_en\n"); + return -EINVAL; + } + + if (((dtsec->dtsec_drv_param)->tx_preamble || + (dtsec->dtsec_drv_param)->rx_preamble) && + ((dtsec->dtsec_drv_param)->preamble_len != 0x7)) { + pr_err("Preamble length should be 0x7 bytes\n"); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_on && + (dtsec->dtsec_drv_param->tx_time_stamp_en || + dtsec->dtsec_drv_param->rx_time_stamp_en)) { + pr_err("1588 timeStamp disabled in half duplex mode\n"); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->rx_flow && + (dtsec->dtsec_drv_param)->rx_ctrl_acc) { + pr_err("Receive control frame can not be accepted\n"); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->rx_prepend > + MAX_PACKET_ALIGNMENT) { + pr_err("packetAlignmentPadding can't be > than %d\n", + MAX_PACKET_ALIGNMENT); + return -EINVAL; + } + if (((dtsec->dtsec_drv_param)->non_back_to_back_ipg1 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->non_back_to_back_ipg2 > + MAX_INTER_PACKET_GAP) || + ((dtsec->dtsec_drv_param)->back_to_back_ipg > + MAX_INTER_PACKET_GAP)) { + pr_err("Inter packet gap can't be greater than %d\n", + MAX_INTER_PACKET_GAP); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_alt_backoff_val > + MAX_INTER_PALTERNATE_BEB) { + pr_err("alternateBackoffVal can't be greater than %d\n", + MAX_INTER_PALTERNATE_BEB); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_retransmit > + MAX_RETRANSMISSION) { + pr_err("maxRetransmission can't be greater than %d\n", + MAX_RETRANSMISSION); + return -EINVAL; + } + if ((dtsec->dtsec_drv_param)->halfdup_coll_window > + MAX_COLLISION_WINDOW) { + pr_err("collisionWindow can't be greater than %d\n", + MAX_COLLISION_WINDOW); + return -EINVAL; + /* If Auto negotiation process is disabled, need to set up the PHY + * using the MII Management Interface + */ + } + if (dtsec->dtsec_drv_param->tbipa > MAX_PHYS) { + pr_err("PHY address (should be 0-%d)\n", MAX_PHYS); + return -ERANGE; + } + if (!dtsec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!dtsec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + /* FM_LEN_CHECK_ERRATA_FMAN_SW002 Errata workaround */ + if (dtsec->dtsec_drv_param->rx_len_check) { + pr_warn("Length Check!\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fm_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_1G_BAB_RX: + bit_mask = DTSEC_IMASK_BREN; + break; + case FM_MAC_EX_1G_RX_CTL: + bit_mask = DTSEC_IMASK_RXCEN; + break; + case FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GTSCEN; + break; + case FM_MAC_EX_1G_BAB_TX: + bit_mask = DTSEC_IMASK_BTEN; + break; + case FM_MAC_EX_1G_TX_CTL: + bit_mask = DTSEC_IMASK_TXCEN; + break; + case FM_MAC_EX_1G_TX_ERR: + bit_mask = DTSEC_IMASK_TXEEN; + break; + case FM_MAC_EX_1G_LATE_COL: + bit_mask = DTSEC_IMASK_LCEN; + break; + case FM_MAC_EX_1G_COL_RET_LMT: + bit_mask = DTSEC_IMASK_CRLEN; + break; + case FM_MAC_EX_1G_TX_FIFO_UNDRN: + bit_mask = DTSEC_IMASK_XFUNEN; + break; + case FM_MAC_EX_1G_MAG_PCKT: + bit_mask = DTSEC_IMASK_MAGEN; + break; + case FM_MAC_EX_1G_MII_MNG_RD_COMPLET: + bit_mask = DTSEC_IMASK_MMRDEN; + break; + case FM_MAC_EX_1G_MII_MNG_WR_COMPLET: + bit_mask = DTSEC_IMASK_MMWREN; + break; + case FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET: + bit_mask = DTSEC_IMASK_GRSCEN; + break; + case FM_MAC_EX_1G_DATA_ERR: + bit_mask = DTSEC_IMASK_TDPEEN; + break; + case FM_MAC_EX_1G_RX_MIB_CNT_OVFL: + bit_mask = DTSEC_IMASK_MSROEN; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static bool is_init_done(struct dtsec_cfg *dtsec_drv_params) +{ + /* Checks if dTSEC driver parameters were initialized */ + if (!dtsec_drv_params) + return true; + + return false; +} + +static u32 get_mac_addr_hash_code(u64 eth_addr) +{ + u32 crc; + + /* CRC calculation */ + GET_MAC_ADDR_CRC(eth_addr, crc); + + crc = bitrev32(crc); + + return crc; +} + +static u16 dtsec_get_max_frame_length(struct dtsec_t *dtsec) +{ + if (is_init_done(dtsec->dtsec_drv_param)) + return 0; + + return fman_dtsec_get_max_frame_len(dtsec->regs); +} + +static void dtsec_isr(void *handle) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)handle; + u32 event; + struct dtsec_regs __iomem *dtsec_regs = dtsec->regs; + + /* do not handle MDIO events */ + event = fman_dtsec_get_event(dtsec_regs, + (u32)(~(DTSEC_IMASK_MMRDEN | + DTSEC_IMASK_MMWREN))); + + event &= fman_dtsec_get_interrupt_mask(dtsec_regs); + + fman_dtsec_ack_event(dtsec_regs, event); + + if (event & DTSEC_IMASK_BREN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_RX); + if (event & DTSEC_IMASK_RXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_RX_CTL); + if (event & DTSEC_IMASK_GTSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET); + if (event & DTSEC_IMASK_BTEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_BAB_TX); + if (event & DTSEC_IMASK_TXCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_CTL); + if (event & DTSEC_IMASK_TXEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_ERR); + if (event & DTSEC_IMASK_LCEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_LATE_COL); + if (event & DTSEC_IMASK_CRLEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_COL_RET_LMT); + if (event & DTSEC_IMASK_XFUNEN) { + /* FM_TX_LOCKUP_ERRATA_DTSEC6 Errata workaround */ + if (dtsec->fm_rev_info.major_rev == 2) { + u32 tpkt1, tmp_reg1, tpkt2, tmp_reg2, i; + /* a. Write 0x00E0_0C00 to DTSEC_ID + * This is a read only register + * b. Read and save the value of TPKT + */ + tpkt1 = in_be32(&dtsec_regs->tpkt); + + /* c. Read the register at dTSEC address offset 0x32C */ + tmp_reg1 = in_be32(&dtsec_regs->reserved02c0[27]); + + /* d. Compare bits [9:15] to bits [25:31] of the + * register at address offset 0x32C. + */ + if ((tmp_reg1 & 0x007F0000) != + (tmp_reg1 & 0x0000007F)) { + /* If they are not equal, save the value of + * this register and wait for at least + * MAXFRM*16 ns + */ + usleep_range((u32)(min + (dtsec_get_max_frame_length(dtsec) * + 16 / 1000, 1)), (u32) + (min(dtsec_get_max_frame_length + (dtsec) * 16 / 1000, 1) + 1)); + } + + /* e. Read and save TPKT again and read the register + * at dTSEC address offset 0x32C again + */ + tpkt2 = in_be32(&dtsec_regs->tpkt); + tmp_reg2 = in_be32(&dtsec_regs->reserved02c0[27]); + + /* f. Compare the value of TPKT saved in step b to + * value read in step e. Also compare bits [9:15] of + * the register at offset 0x32C saved in step d to the + * value of bits [9:15] saved in step e. If the two + * registers values are unchanged, then the transmit + * portion of the dTSEC controller is locked up and + * the user should proceed to the recover sequence. + */ + if ((tpkt1 == tpkt2) && ((tmp_reg1 & 0x007F0000) == + (tmp_reg2 & 0x007F0000))) { + /* recover sequence */ + + /* a.Write a 1 to RCTRL[GRS] */ + + out_be32(&dtsec_regs->rctrl, + in_be32(&dtsec_regs->rctrl) | + RCTRL_GRS); + + /* b.Wait until IEVENT[GRSC]=1, or at least + * 100 us has elapsed. + */ + for (i = 0; i < 100; i++) { + if (in_be32(&dtsec_regs->ievent) & + DTSEC_IMASK_GRSCEN) + break; + udelay(1); + } + if (in_be32(&dtsec_regs->ievent) & + DTSEC_IMASK_GRSCEN) + out_be32(&dtsec_regs->ievent, + DTSEC_IMASK_GRSCEN); + else + pr_debug("Rx lockup due to Tx lockup\n"); + + /* c.Write a 1 to bit n of FM_RSTC + * (offset 0x0CC of FPM) + */ + fm_reset_mac(dtsec->fm, dtsec->mac_id); + + /* d.Wait 4 Tx clocks (32 ns) */ + udelay(1); + + /* e.Write a 0 to bit n of FM_RSTC. */ + /* cleared by FMAN + */ + } + } + + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_TX_FIFO_UNDRN); + } + if (event & DTSEC_IMASK_MAGEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_MAG_PCKT); + if (event & DTSEC_IMASK_GRSCEN) + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET); + if (event & DTSEC_IMASK_TDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_EX_1G_DATA_ERR); + if (event & DTSEC_IMASK_RDPEEN) + dtsec->exception_cb(dtsec->dev_id, FM_MAC_1G_RX_DATA_ERR); + + /* masked interrupts */ + WARN_ON(event & DTSEC_IMASK_ABRTEN); + WARN_ON(event & DTSEC_IMASK_IFERREN); +} + +static void dtsec_1588_isr(void *handle) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)handle; + u32 event; + struct dtsec_regs __iomem *dtsec_regs = dtsec->regs; + + if (dtsec->ptp_tsu_enabled) { + event = fman_dtsec_check_and_clear_tmr_event(dtsec_regs); + + if (event) { + WARN_ON(event & TMR_PEVENT_TSRE); + dtsec->exception_cb(dtsec->dev_id, + FM_MAC_EX_1G_1588_TS_RX_ERR); + } + } +} + +static void free_init_resources(struct dtsec_t *dtsec) +{ + fm_unregister_intr(dtsec->fm, FM_MOD_MAC, dtsec->mac_id, + FM_INTR_TYPE_ERR); + fm_unregister_intr(dtsec->fm, FM_MOD_MAC, dtsec->mac_id, + FM_INTR_TYPE_NORMAL); + + /* release the driver's group hash table */ + free_hash_table(dtsec->multicast_addr_hash); + dtsec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(dtsec->unicast_addr_hash); + dtsec->unicast_addr_hash = NULL; +} + +static int graceful_stop(struct dtsec_t *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs; + + regs = dtsec->regs; + + /* Assert the graceful transmit stop bit */ + if (mode & COMM_MODE_RX) { + fman_dtsec_stop_rx(regs); + + if (dtsec->fm_rev_info.major_rev == 2) + usleep_range(100, 200); + else + udelay(10); + } + + if (mode & COMM_MODE_TX) { + if (dtsec->fm_rev_info.major_rev == 2) + pr_debug("GTS not supported due to DTSEC_A004 errata.\n"); + else + pr_debug("GTS not supported due to DTSEC_A0014 errata.\n"); + } + return 0; +} + +static int graceful_restart(struct dtsec_t *dtsec, enum comm_mode mode) +{ + struct dtsec_regs __iomem *regs; + + regs = dtsec->regs; + /* clear the graceful receive stop bit */ + if (mode & COMM_MODE_TX) + fman_dtsec_start_tx(regs); + + if (mode & COMM_MODE_RX) + fman_dtsec_start_rx(regs); + + return 0; +} + +static int dtsec_mii_write_phy_reg(struct dtsec_t *dtsec, u8 phy_addr, + u8 reg, u16 data) +{ + u16 dtsec_freq; + + dtsec_freq = (u16)(dtsec->clk_freq >> 1); + + return fman_dtsec_mii_write_reg(dtsec->mii_regs, phy_addr, reg, + data, dtsec_freq); +} + +static int dtsec_mii_read_phy_reg(struct dtsec_t *dtsec, u8 phy_addr, + u8 reg, u16 *data) +{ + u16 dtsec_freq; + int err; + + dtsec_freq = (u16)(dtsec->clk_freq >> 1); + + err = fman_dtsec_mii_read_reg(dtsec->mii_regs, phy_addr, reg, + data, dtsec_freq); + + if (*data == 0xffff) { + pr_warn("Read wrong data(0xffff):phy_addr 0x%x,reg 0x%x", + phy_addr, reg); + return -ENXIO; + } + if (err) + return err; + + return err; +} + +int dtsec_cfg_max_frame_len(struct fm_mac_dev *fm_mac_dev, u16 new_val) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->maximum_frame = new_val; + + return 0; +} + +int dtsec_cfg_pad_and_crc(struct fm_mac_dev *fm_mac_dev, bool new_val) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->dtsec_drv_param->tx_pad_crc = new_val; + + return 0; +} + +int dtsec_enable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + fman_dtsec_enable(dtsec->regs, + (bool)!!(mode & COMM_MODE_RX), + (bool)!!(mode & COMM_MODE_TX)); + + graceful_restart(dtsec, mode); + + return 0; +} + +int dtsec_disable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + graceful_stop(dtsec, mode); + + fman_dtsec_disable(dtsec->regs, + (bool)!!(mode & COMM_MODE_RX), + (bool)!!(mode & COMM_MODE_TX)); + + return 0; +} + +int dtsec_set_tx_pause_frames(struct fm_mac_dev *fm_mac_dev, + u8 __maybe_unused priority, + u16 pause_time, u16 __maybe_unused thresh_time) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* FM_BAD_TX_TS_IN_B_2_B_ERRATA_DTSEC_A003 Errata workaround */ + if (dtsec->fm_rev_info.major_rev == 2) + if (0 < pause_time && pause_time <= 320) { + pr_warn("pause-time: %d illegal.Should be > 320\n", + pause_time); + return -EINVAL; + } + + fman_dtsec_set_tx_pause_frames(dtsec->regs, pause_time); + return 0; +} + +int dtsec_accept_rx_pause_frames(struct fm_mac_dev *fm_mac_dev, bool en) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + fman_dtsec_handle_rx_pause(dtsec->regs, en); + + return 0; +} + +int dtsec_modify_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *enet_addr) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + /* Initialize MAC Station Address registers (1 & 2) + * Station address have to be swapped (big endian to little endian + */ + dtsec->addr = ENET_ADDR_TO_UINT64(*enet_addr); + fman_dtsec_set_mac_address(dtsec->regs, (u8 *)(*enet_addr)); + + return 0; +} + +int dtsec_add_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + struct eth_hash_entry_t *hash_entry; + u64 addr; + s32 bucket; + u32 crc; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((fman_dtsec_get_rctrl(dtsec->regs) & + RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = get_mac_addr_hash_code(addr); + + /* considering the 9 highest order bits in crc H[8:0]: + *if ghtx = 0 H[8:6] (highest order 3 bits) identify the hash register + *and H[5:1] (next 5 bits) identify the hash bit + *if ghts = 1 H[8:5] (highest order 4 bits) identify the hash register + *and H[4:0] (next 5 bits) identify the hash bit. + * + *In bucket index output the low 5 bits identify the hash register + *bit, while the higher 4 bits identify the hash register + */ + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set in gaddr instead of + *igaddr. + */ + if (mcast) + bucket += 0x100; + } + + fman_dtsec_set_bucket(dtsec->regs, bucket, true); + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + if (addr & MAC_GROUP_ADDRESS) + /* Group Address */ + list_add_tail(&hash_entry->node, + &dtsec->multicast_addr_hash->lsts[bucket]); + else + list_add_tail(&hash_entry->node, + &dtsec->unicast_addr_hash->lsts[bucket]); + + return 0; +} + +int dtsec_del_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + struct list_head *pos; + struct eth_hash_entry_t *hash_entry = NULL; + u64 addr; + s32 bucket; + u32 crc; + bool mcast, ghtx; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + ghtx = (bool)((fman_dtsec_get_rctrl(dtsec->regs) & + RCTRL_GHTX) ? true : false); + mcast = (bool)((addr & MAC_GROUP_ADDRESS) ? true : false); + + /* Cannot handle unicast mac addr when GHTX is on */ + if (ghtx && !mcast) { + pr_err("Could not compute hash bucket\n"); + return -EINVAL; + } + crc = get_mac_addr_hash_code(addr); + + if (ghtx) { + bucket = (s32)((crc >> 23) & 0x1ff); + } else { + bucket = (s32)((crc >> 24) & 0xff); + /* if !ghtx and mcast the bit must be set + * in gaddr instead of igaddr. + */ + if (mcast) + bucket += 0x100; + } + + if (addr & MAC_GROUP_ADDRESS) { + /* Group Address */ + list_for_each(pos, + &dtsec->multicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->multicast_addr_hash->lsts[bucket])) + fman_dtsec_set_bucket(dtsec->regs, bucket, false); + } else { + /* Individual Address */ + list_for_each(pos, + &dtsec->unicast_addr_hash->lsts[bucket]) { + hash_entry = ETH_HASH_ENTRY_OBJ(pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&dtsec->unicast_addr_hash->lsts[bucket])) + fman_dtsec_set_bucket(dtsec->regs, bucket, false); + } + + /* address does not exist */ + WARN_ON(!hash_entry); + + return 0; +} + +int dtsec_set_promiscuous(struct fm_mac_dev *fm_mac_dev, bool new_val) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + fman_dtsec_set_uc_promisc(dtsec->regs, new_val); + fman_dtsec_set_mc_promisc(dtsec->regs, new_val); + + return 0; +} + +int dtsec_adjust_link(struct fm_mac_dev *fm_mac_dev, enum ethernet_speed speed) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + int err; + enum enet_interface enet_interface; + enum enet_speed enet_speed; + int full_duplex = true; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec->enet_mode = + MAKE_ENET_MODE(ENET_INTERFACE_FROM_MODE(dtsec->enet_mode), speed); + enet_interface = + (enum enet_interface)ENET_INTERFACE_FROM_MODE(dtsec->enet_mode); + enet_speed = (enum enet_speed)ENET_SPEED_FROM_MODE(dtsec->enet_mode); + + err = fman_dtsec_adjust_link(dtsec->regs, enet_interface, + enet_speed, full_duplex); + + if (err == -EINVAL) { + pr_err("Ethernet doesn't support Half Duplex mode\n"); + return -EINVAL; + } + + return err; +} + +int dtsec_restart_autoneg(struct fm_mac_dev *fm_mac_dev) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + u16 tmp_reg16; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + dtsec_mii_read_phy_reg(dtsec, dtsec->tbi_phy_addr, 0, &tmp_reg16); + + tmp_reg16 &= ~(PHY_CR_SPEED0 | PHY_CR_SPEED1); + tmp_reg16 |= + (PHY_CR_ANE | PHY_CR_RESET_AN | PHY_CR_FULLDUPLEX | PHY_CR_SPEED1); + + dtsec_mii_write_phy_reg(dtsec, dtsec->tbi_phy_addr, 0, tmp_reg16); + + return 0; +} + +int dtsec_get_version(struct fm_mac_dev *fm_mac_dev, u32 *mac_version) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + *mac_version = fman_dtsec_get_revision(dtsec->regs); + + return 0; +} + +int dtsec_set_exception(struct fm_mac_dev *fm_mac_dev, + enum fm_mac_exceptions exception, + bool enable) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + u32 bit_mask = 0; + + if (!is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (exception != FM_MAC_EX_1G_1588_TS_RX_ERR) { + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + dtsec->exceptions |= bit_mask; + else + dtsec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + fman_dtsec_enable_interrupt(dtsec->regs, bit_mask); + else + fman_dtsec_disable_interrupt(dtsec->regs, bit_mask); + } else { + if (!dtsec->ptp_tsu_enabled) { + pr_err("Exception valid for 1588 only\n"); + return -EINVAL; + } + switch (exception) { + case FM_MAC_EX_1G_1588_TS_RX_ERR: + if (enable) { + dtsec->en_tsu_err_exeption = true; + fman_dtsec_enable_tmr_interrupt(dtsec->regs); + } else { + dtsec->en_tsu_err_exeption = false; + fman_dtsec_disable_tmr_interrupt(dtsec->regs); + } + break; + default: + pr_err("Undefined exception\n"); + return -EINVAL; + } + } + + return 0; +} + +int dtsec_init(struct fm_mac_dev *fm_mac_dev) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + struct dtsec_cfg *dtsec_drv_param; + int err; + u16 max_frm_ln; + enum enet_interface enet_interface; + enum enet_speed enet_speed; + enet_addr_t eth_addr; + + if (is_init_done(dtsec->dtsec_drv_param)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fm_reset_mac(dtsec->fm, dtsec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + err = check_init_parameters(dtsec); + if (err) + return err; + + dtsec_drv_param = dtsec->dtsec_drv_param; + + enet_interface = + (enum enet_interface)ENET_INTERFACE_FROM_MODE(dtsec->enet_mode); + enet_speed = (enum enet_speed)ENET_SPEED_FROM_MODE(dtsec->enet_mode); + MAKE_ENET_ADDR_FROM_UINT64(dtsec->addr, eth_addr); + + err = fman_dtsec_init(dtsec->regs, dtsec_drv_param, enet_interface, + enet_speed, (u8 *)eth_addr, + dtsec->fm_rev_info.major_rev, + dtsec->fm_rev_info.minor_rev, + dtsec->exceptions); + if (err) { + free_init_resources(dtsec); + pr_err("DTSEC version doesn't support this i/f mode\n"); + return err; + } + + if (ENET_INTERFACE_FROM_MODE(dtsec->enet_mode) == ENET_IF_SGMII) { + u16 tmp_reg16; + + /* Configure the TBI PHY Control Register */ + tmp_reg16 = PHY_TBICON_CLK_SEL | PHY_TBICON_SRESET; + dtsec_mii_write_phy_reg(dtsec, (u8)dtsec_drv_param->tbipa, + 17, tmp_reg16); + + tmp_reg16 = PHY_TBICON_CLK_SEL; + dtsec_mii_write_phy_reg(dtsec, (u8)dtsec_drv_param->tbipa, + 17, tmp_reg16); + + tmp_reg16 = + (PHY_CR_PHY_RESET | PHY_CR_ANE | PHY_CR_FULLDUPLEX | + PHY_CR_SPEED1); + dtsec_mii_write_phy_reg(dtsec, (u8)dtsec_drv_param->tbipa, + 0, tmp_reg16); + + if (dtsec->enet_mode & ENET_IF_SGMII_BASEX) + tmp_reg16 = PHY_TBIANA_1000X; + else + tmp_reg16 = PHY_TBIANA_SGMII; + dtsec_mii_write_phy_reg(dtsec, (u8)dtsec_drv_param->tbipa, + 4, tmp_reg16); + + tmp_reg16 = + (PHY_CR_ANE | PHY_CR_RESET_AN | PHY_CR_FULLDUPLEX | + PHY_CR_SPEED1); + + dtsec_mii_write_phy_reg(dtsec, (u8)dtsec_drv_param->tbipa, + 0, tmp_reg16); + } + + /* Max Frame Length */ + max_frm_ln = fman_dtsec_get_max_frame_len(dtsec->regs); + err = fm_set_mac_max_frame(dtsec->fm, FM_MAC_1G, dtsec->mac_id, + max_frm_ln); + if (err) { + pr_err("Setting max frame length failed\n"); + free_init_resources(dtsec); + return -EINVAL; + } + + dtsec->multicast_addr_hash = + alloc_hash_table(EXTENDED_HASH_TABLE_SIZE); + if (!dtsec->multicast_addr_hash) { + free_init_resources(dtsec); + pr_err("MC hash table is failed\n"); + return -ENOMEM; + } + + dtsec->unicast_addr_hash = alloc_hash_table(DTSEC_HASH_TABLE_SIZE); + if (!dtsec->unicast_addr_hash) { + free_init_resources(dtsec); + pr_err("UC hash table is failed\n"); + return -ENOMEM; + } + + /* register err intr handler for dtsec to FPM (err) */ + fm_register_intr(dtsec->fm, FM_MOD_MAC, dtsec->mac_id, + FM_INTR_TYPE_ERR, dtsec_isr, dtsec); + /* register 1588 intr handler for TMR to FPM (normal) */ + fm_register_intr(dtsec->fm, FM_MOD_MAC, dtsec->mac_id, + FM_INTR_TYPE_NORMAL, dtsec_1588_isr, dtsec); + + kfree(dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + + return 0; +} + +int dtsec_free(struct fm_mac_dev *fm_mac_dev) +{ + struct dtsec_t *dtsec = (struct dtsec_t *)fm_mac_dev; + + free_init_resources(dtsec); + + kfree(dtsec->dtsec_drv_param); + dtsec->dtsec_drv_param = NULL; + kfree(dtsec); + + return 0; +} + +void *dtsec_config(struct fm_mac_params_t *fm_mac_param) +{ + struct dtsec_t *dtsec; + struct dtsec_cfg *dtsec_drv_param; + void __iomem *base_addr; + + base_addr = fm_mac_param->base_addr; + + /* allocate memory for the UCC GETH data structure. */ + dtsec = kzalloc(sizeof(*dtsec), GFP_KERNEL); + if (!dtsec) + return NULL; + + /* allocate memory for the d_tsec driver parameters data structure. */ + dtsec_drv_param = kzalloc(sizeof(*dtsec_drv_param), GFP_KERNEL); + if (!dtsec_drv_param) + goto err_dtsec; + + /* Plant parameter structure pointer */ + dtsec->dtsec_drv_param = dtsec_drv_param; + + fman_dtsec_defconfig(dtsec_drv_param); + + dtsec->regs = (struct dtsec_regs __iomem *)(base_addr); + dtsec->mii_regs = (struct dtsec_mii_reg __iomem *) + (base_addr + DTSEC_TO_MII_OFFSET); + dtsec->addr = ENET_ADDR_TO_UINT64(fm_mac_param->addr); + dtsec->enet_mode = fm_mac_param->enet_mode; + dtsec->mac_id = fm_mac_param->mac_id; + dtsec->exceptions = DTSEC_DEFAULT_EXCEPTIONS; + dtsec->exception_cb = fm_mac_param->exception_cb; + dtsec->event_cb = fm_mac_param->event_cb; + dtsec->dev_id = fm_mac_param->dev_id; + dtsec->ptp_tsu_enabled = dtsec->dtsec_drv_param->ptp_tsu_en; + dtsec->en_tsu_err_exeption = dtsec->dtsec_drv_param->ptp_exception_en; + dtsec->tbi_phy_addr = dtsec->dtsec_drv_param->tbi_phy_addr; + + dtsec->fm = fm_mac_param->fm; + dtsec->clk_freq = fm_get_clock_freq(dtsec->fm); + if (dtsec->clk_freq == 0) { + pr_err("Can't get clock for MAC!\n"); + goto err_dtsec; + } + + /* Save FMan revision */ + fm_get_revision(dtsec->fm, &dtsec->fm_rev_info); + + return dtsec; + +err_dtsec: + kfree(dtsec); + return NULL; +} diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_dtsec.h b/drivers/net/ethernet/freescale/fman/mac/fm_dtsec.h new file mode 100644 index 0000000..1ed7295 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_dtsec.h @@ -0,0 +1,207 @@ +/* + * Copyright 2008-2015 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 __DTSEC_H +#define __DTSEC_H + +#include "enet_ext.h" + +#include "fm_mac.h" + +#define DTSEC_DEFAULT_EXCEPTIONS \ + ((u32)((DTSEC_IMASK_BREN) |\ + (DTSEC_IMASK_RXCEN) |\ + (DTSEC_IMASK_BTEN) |\ + (DTSEC_IMASK_TXCEN) |\ + (DTSEC_IMASK_TXEEN) |\ + (DTSEC_IMASK_ABRTEN) |\ + (DTSEC_IMASK_LCEN) |\ + (DTSEC_IMASK_CRLEN) |\ + (DTSEC_IMASK_XFUNEN) |\ + (DTSEC_IMASK_IFERREN) |\ + (DTSEC_IMASK_MAGEN) |\ + (DTSEC_IMASK_TDPEEN) |\ + (DTSEC_IMASK_RDPEEN))) + +#define MAX_PACKET_ALIGNMENT 31 +#define MAX_INTER_PACKET_GAP 0x7f +#define MAX_INTER_PALTERNATE_BEB 0x0f +#define MAX_RETRANSMISSION 0x0f +#define MAX_COLLISION_WINDOW 0x03ff + +/* number of pattern match registers (entries) */ +#define DTSEC_NUM_OF_PADDRS 15 + +/* Group address bit indication */ +#define GROUP_ADDRESS 0x0000010000000000LL + +/* Hash table size (32 bits*8 regs) */ +#define DTSEC_HASH_TABLE_SIZE 256 +/* Extended Hash table size (32 bits*16 regs) */ +#define EXTENDED_HASH_TABLE_SIZE 512 + +/* number of pattern match registers (entries) */ +#define DTSEC_TO_MII_OFFSET 0x1000 +/* maximum number of phys */ +#define MAX_PHYS 32 + +#define VAL32BIT 0x100000000LL +#define VAL22BIT 0x00400000 +#define VAL16BIT 0x00010000 +#define VAL12BIT 0x00001000 + +/* CAR1/2 bits */ +#define CAR1_TR64 0x80000000 +#define CAR1_TR127 0x40000000 +#define CAR1_TR255 0x20000000 +#define CAR1_TR511 0x10000000 +#define CAR1_TRK1 0x08000000 +#define CAR1_TRMAX 0x04000000 +#define CAR1_TRMGV 0x02000000 + +#define CAR1_RBYT 0x00010000 +#define CAR1_RPKT 0x00008000 +#define CAR1_RMCA 0x00002000 +#define CAR1_RBCA 0x00001000 +#define CAR1_RXPF 0x00000400 +#define CAR1_RALN 0x00000100 +#define CAR1_RFLR 0x00000080 +#define CAR1_RCDE 0x00000040 +#define CAR1_RCSE 0x00000020 +#define CAR1_RUND 0x00000010 +#define CAR1_ROVR 0x00000008 +#define CAR1_RFRG 0x00000004 +#define CAR1_RJBR 0x00000002 +#define CAR1_RDRP 0x00000001 + +#define CAR2_TFCS 0x00040000 +#define CAR2_TBYT 0x00002000 +#define CAR2_TPKT 0x00001000 +#define CAR2_TMCA 0x00000800 +#define CAR2_TBCA 0x00000400 +#define CAR2_TXPF 0x00000200 +#define CAR2_TDRP 0x00000001 + +struct internal_statistics_t { + u64 tr64; + u64 tr127; + u64 tr255; + u64 tr511; + u64 tr1k; + u64 trmax; + u64 trmgv; + u64 rfrg; + u64 rjbr; + u64 rdrp; + u64 raln; + u64 rund; + u64 rovr; + u64 rxpf; + u64 txpf; + u64 rbyt; + u64 rpkt; + u64 rmca; + u64 rbca; + u64 rflr; + u64 rcde; + u64 rcse; + u64 tbyt; + u64 tpkt; + u64 tmca; + u64 tbca; + u64 tdrp; + u64 tfcs; +}; + +struct dtsec_t { + /* pointer to dTSEC memory mapped registers */ + struct dtsec_regs __iomem *regs; + /* pointer to dTSEC MII memory mapped registers */ + struct dtsec_mii_reg __iomem *mii_regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + enum e_enet_mode enet_mode; + void *dev_id; /* device cookie used by the exception cbs */ + fm_mac_exception_cb *exception_cb; + fm_mac_exception_cb *event_cb; + /* Whether a particular individual address recognition + * register is being used + */ + bool ind_addr_reg_used[DTSEC_NUM_OF_PADDRS]; + /* MAC address for particular individual + * address recognition register + */ + u64 paddr[DTSEC_NUM_OF_PADDRS]; + /* Number of individual addresses in registers for this station */ + u8 num_of_ind_addr_in_regs; + struct internal_statistics_t internal_statistics; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + u8 mac_id; + u8 tbi_phy_addr; + u32 exceptions; + bool ptp_tsu_enabled; + bool en_tsu_err_exeption; + struct dtsec_cfg *dtsec_drv_param; + u16 clk_freq; + void *fm; + struct fm_revision_info_t fm_rev_info; +}; + +void *dtsec_config(struct fm_mac_params_t *fm_mac_param); +int dtsec_set_promiscuous(struct fm_mac_dev *fm_mac_dev, bool new_val); +int dtsec_modify_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *enet_addr); +int dtsec_adjust_link(struct fm_mac_dev *fm_mac_dev, + enum ethernet_speed speed); +int dtsec_restart_autoneg(struct fm_mac_dev *fm_mac_dev); +int dtsec_cfg_max_frame_len(struct fm_mac_dev *fm_mac_dev, u16 new_val); +int dtsec_cfg_pad_and_crc(struct fm_mac_dev *fm_mac_dev, bool new_val); +int dtsec_enable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode); +int dtsec_disable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode); +int dtsec_init(struct fm_mac_dev *fm_mac_dev); +int dtsec_free(struct fm_mac_dev *fm_mac_dev); +int dtsec_accept_rx_pause_frames(struct fm_mac_dev *fm_mac_dev, bool en); +int dtsec_set_tx_pause_frames(struct fm_mac_dev *fm_mac_dev, u8 priority, + u16 pause_time, u16 thresh_time); +int dtsec_set_exception(struct fm_mac_dev *fm_mac_dev, + enum fm_mac_exceptions exception, bool enable); +int dtsec_add_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr); +int dtsec_del_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr); +int dtsec_get_version(struct fm_mac_dev *fm_mac_dev, u32 *mac_version); + +#endif /* __DTSEC_H */ diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_mac.h b/drivers/net/ethernet/freescale/fman/mac/fm_mac.h new file mode 100644 index 0000000..6004e2d --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_mac.h @@ -0,0 +1,259 @@ +/* + * Copyright 2008-2015 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. + */ + +/* FM MAC ... */ +#ifndef __FM_MAC_H +#define __FM_MAC_H + +#include "enet_ext.h" +#include "fm_common.h" + +#include + +struct fm_mac_dev; + +/* defaults */ +#define DEFAULT_RESET_ON_INIT false + +/* PFC defines */ +#define FSL_FM_PAUSE_TIME_ENABLE 0xf000 +#define FSL_FM_PAUSE_TIME_DISABLE 0 +#define FSL_FM_PAUSE_THRESH_DEFAULT 0 + +#define FM_MAC_NO_PFC 0xff + +/* HASH defines */ +#define ETH_HASH_ENTRY_OBJ(ptr) \ + hlist_entry_safe(ptr, struct eth_hash_entry_t, node) + +/* Enumeration (bit flags) of communication modes (Transmit, + * receive or both). + */ +enum comm_mode { + COMM_MODE_NONE = 0, /* No transmit/receive communication */ + COMM_MODE_RX = 1, /* Only receive communication */ + COMM_MODE_TX = 2, /* Only transmit communication */ + COMM_MODE_RX_AND_TX = 3 /* Both transmit and receive communication */ +}; + +/* FM MAC Exceptions */ +enum fm_mac_exceptions { + FM_MAC_EX_10G_MDIO_SCAN_EVENT = 0 + /* 10GEC MDIO scan event interrupt */ + , FM_MAC_EX_10G_MDIO_CMD_CMPL + /* 10GEC MDIO command completion interrupt */ + , FM_MAC_EX_10G_REM_FAULT + /* 10GEC, mEMAC Remote fault interrupt */ + , FM_MAC_EX_10G_LOC_FAULT + /* 10GEC, mEMAC Local fault interrupt */ + , FM_MAC_EX_10G_TX_ECC_ER + /* 10GEC, mEMAC Transmit frame ECC error interrupt */ + , FM_MAC_EX_10G_TX_FIFO_UNFL + /* 10GEC, mEMAC Transmit FIFO underflow interrupt */ + , FM_MAC_EX_10G_TX_FIFO_OVFL + /* 10GEC, mEMAC Transmit FIFO overflow interrupt */ + , FM_MAC_EX_10G_TX_ER + /* 10GEC Transmit frame error interrupt */ + , FM_MAC_EX_10G_RX_FIFO_OVFL + /* 10GEC, mEMAC Receive FIFO overflow interrupt */ + , FM_MAC_EX_10G_RX_ECC_ER + /* 10GEC, mEMAC Receive frame ECC error interrupt */ + , FM_MAC_EX_10G_RX_JAB_FRM + /* 10GEC Receive jabber frame interrupt */ + , FM_MAC_EX_10G_RX_OVRSZ_FRM + /* 10GEC Receive oversized frame interrupt */ + , FM_MAC_EX_10G_RX_RUNT_FRM + /* 10GEC Receive runt frame interrupt */ + , FM_MAC_EX_10G_RX_FRAG_FRM + /* 10GEC Receive fragment frame interrupt */ + , FM_MAC_EX_10G_RX_LEN_ER + /* 10GEC Receive payload length error interrupt */ + , FM_MAC_EX_10G_RX_CRC_ER + /* 10GEC Receive CRC error interrupt */ + , FM_MAC_EX_10G_RX_ALIGN_ER + /* 10GEC Receive alignment error interrupt */ + , FM_MAC_EX_1G_BAB_RX + /* dTSEC Babbling receive error */ + , FM_MAC_EX_1G_RX_CTL + /* dTSEC Receive control (pause frame) interrupt */ + , FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET + /* dTSEC Graceful transmit stop complete */ + , FM_MAC_EX_1G_BAB_TX + /* dTSEC Babbling transmit error */ + , FM_MAC_EX_1G_TX_CTL + /* dTSEC Transmit control (pause frame) interrupt */ + , FM_MAC_EX_1G_TX_ERR + /* dTSEC Transmit error */ + , FM_MAC_EX_1G_LATE_COL + /* dTSEC Late collision */ + , FM_MAC_EX_1G_COL_RET_LMT + /* dTSEC Collision retry limit */ + , FM_MAC_EX_1G_TX_FIFO_UNDRN + /* dTSEC Transmit FIFO underrun */ + , FM_MAC_EX_1G_MAG_PCKT + /* dTSEC Magic Packet detection */ + , FM_MAC_EX_1G_MII_MNG_RD_COMPLET + /* dTSEC MII management read completion */ + , FM_MAC_EX_1G_MII_MNG_WR_COMPLET + /* dTSEC MII management write completion */ + , FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET + /* dTSEC Graceful receive stop complete */ + , FM_MAC_EX_1G_DATA_ERR + /* dTSEC Internal data error on transmit */ + , FM_MAC_1G_RX_DATA_ERR + /* dTSEC Internal data error on receive */ + , FM_MAC_EX_1G_1588_TS_RX_ERR + /* dTSEC Time-Stamp Receive Error */ + , FM_MAC_EX_1G_RX_MIB_CNT_OVFL + /* dTSEC MIB counter overflow */ + , FM_MAC_EX_TS_FIFO_ECC_ERR + /* mEMAC Time-stamp FIFO ECC error interrupt; + * not supported on T4240/B4860 rev1 chips + */ + , FM_MAC_EX_MAGIC_PACKET_INDICATION = FM_MAC_EX_1G_MAG_PCKT + /* mEMAC Magic Packet Indication Interrupt */ +}; + +struct eth_hash_entry_t { + u64 addr; /* Ethernet Address */ + struct list_head node; +}; + +typedef void (fm_mac_exception_cb)(void *dev_id, + enum fm_mac_exceptions exceptions); + +/* FM MAC config input */ +struct fm_mac_params_t { + /* Base of memory mapped FM MAC registers */ + void __iomem *base_addr; + /* MAC address of device; First octet is sent first */ + enet_addr_t addr; + /* MAC ID; numbering of dTSEC and 1G-mEMAC: + * 0 - FM_MAX_NUM_OF_1G_MACS; + * numbering of 10G-MAC (TGEC) and 10G-mEMAC: + * 0 - FM_MAX_NUM_OF_10G_MACS + */ + u8 mac_id; + /* Ethernet operation mode (MAC-PHY interface and speed); + * Note that the speed should indicate the maximum rate that + * this MAC should support rather than the actual speed; + * i.e. user should use the FM_MAC_AdjustLink() routine to + * provide accurate speed; + * In case of mEMAC RGMII mode, the MAC is configured to RGMII + * automatic mode, where actual speed/duplex mode information + * is provided by PHY automatically in-band; FM_MAC_AdjustLink() + * function should be used to switch to manual RGMII speed/duplex mode + * configuration if RGMII PHY doesn't support in-band status signaling; + * In addition, in mEMAC, in case where user is using the higher MACs + * (i.e. the MACs that should support 10G), user should pass here + * speed=10000 even if the interface is not allowing that (e.g. SGMII). + */ + enum e_enet_mode enet_mode; + /* A handle to the FM object this port related to */ + void *fm; + /* MDIO exceptions interrupt source - not valid for all + * MACs; MUST be set to 'NO_IRQ' for MACs that don't have + * mdio-irq, or for polling + */ + void *dev_id; /* device cookie used by the exception cbs */ + fm_mac_exception_cb *event_cb; /* MDIO Events Callback Routine */ + fm_mac_exception_cb *exception_cb; /* Exception Callback Routine */ +}; + +struct eth_hash_t { + u16 size; + struct list_head *lsts; +}; + +static inline struct eth_hash_entry_t +*dequeue_addr_from_hash_entry(struct list_head *addr_lst) +{ + struct eth_hash_entry_t *hash_entry = NULL; + + if (!list_empty(addr_lst)) { + hash_entry = ETH_HASH_ENTRY_OBJ(addr_lst->next); + list_del_init(&hash_entry->node); + } + return hash_entry; +} + +static inline void free_hash_table(struct eth_hash_t *hash) +{ + struct eth_hash_entry_t *hash_entry; + int i = 0; + + if (hash) { + if (hash->lsts) { + for (i = 0; i < hash->size; i++) { + hash_entry = + dequeue_addr_from_hash_entry(&hash->lsts[i]); + while (hash_entry) { + kfree(hash_entry); + hash_entry = + dequeue_addr_from_hash_entry(&hash-> + lsts[i]); + } + } + + kfree(hash->lsts); + } + + kfree(hash); + } +} + +static inline struct eth_hash_t *alloc_hash_table(u16 size) +{ + u32 i; + struct eth_hash_t *hash; + + /* Allocate address hash table */ + hash = kmalloc_array(size, sizeof(struct eth_hash_t *), GFP_KERNEL); + if (!hash) + return NULL; + + hash->size = size; + + hash->lsts = kmalloc_array(hash->size, sizeof(struct list_head), + GFP_KERNEL); + if (!hash->lsts) { + kfree(hash); + return NULL; + } + + for (i = 0; i < hash->size; i++) + INIT_LIST_HEAD(&hash->lsts[i]); + + return hash; +} + +#endif /* __FM_MAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_memac.c b/drivers/net/ethernet/freescale/fman/mac/fm_memac.c new file mode 100644 index 0000000..54782c3 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_memac.c @@ -0,0 +1,673 @@ +/* + * Copyright 2008-2015 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "fm_common.h" +#include "fm_memac.h" + +#include +#include + +static u32 get_mac_addr_hash_code(u64 eth_addr) +{ + u64 mask1, mask2; + u32 xor_val = 0; + u8 i, j; + + for (i = 0; i < 6; i++) { + mask1 = eth_addr & (u64)0x01; + eth_addr >>= 1; + + for (j = 0; j < 7; j++) { + mask2 = eth_addr & (u64)0x01; + mask1 ^= mask2; + eth_addr >>= 1; + } + + xor_val |= (mask1 << (5 - i)); + } + + return xor_val; +} + +static int memac_mii_write_phy_reg(struct memac_t *memac, u8 phy_addr, + u8 reg, u16 data) +{ + return fman_memac_mii_write_phy_reg(memac->mii_regs, + phy_addr, reg, data, + (enum enet_speed) + ENET_SPEED_FROM_MODE(memac-> + enet_mode)); +} + +static void setup_sgmii_internal_phy(struct memac_t *memac, u8 phy_addr) +{ + u16 tmp_reg16; + enum e_enet_mode enet_mode; + + /* In case the higher MACs are used (i.e. the MACs that should + * support 10G), speed=10000 is provided for SGMII ports. + * Temporary modify enet mode to 1G one, so MII functions can + * work correctly. + */ + enet_mode = memac->enet_mode; + memac->enet_mode = + MAKE_ENET_MODE(ENET_INTERFACE_FROM_MODE(memac->enet_mode), + ENET_SPEED_1000); + + /* SGMII mode + AN enable */ + tmp_reg16 = PHY_SGMII_IF_MODE_AN | PHY_SGMII_IF_MODE_SGMII; + memac_mii_write_phy_reg(memac, phy_addr, 0x14, tmp_reg16); + + /* Device ability according to SGMII specification */ + tmp_reg16 = PHY_SGMII_DEV_ABILITY_SGMII; + memac_mii_write_phy_reg(memac, phy_addr, 0x4, tmp_reg16); + + /* Adjust link timer for SGMII - + * According to Cisco SGMII specification the timer should be 1.6 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 1.6 ms in units of 8 ns = 1.6ms / 8ns = 2*10^5 = 0x30d40 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 1.6 ms in units of 3.2 ns = 1.6ms / 3.2ns = 5*10^5 = 0x7a120. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + memac_mii_write_phy_reg(memac, phy_addr, 0x13, 0x0007); + memac_mii_write_phy_reg(memac, phy_addr, 0x12, 0xa120); + + /* Restart AN */ + tmp_reg16 = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN; + memac_mii_write_phy_reg(memac, phy_addr, 0x0, tmp_reg16); + + /* Restore original enet mode */ + memac->enet_mode = enet_mode; +} + +static void setup_sgmii_internal_phy_base_x(struct memac_t *memac, + u8 phy_addr) +{ + u16 tmp_reg16; + enum e_enet_mode enet_mode; + + /* In case the higher MACs are used (i.e. the MACs that + * should support 10G), speed=10000 is provided for SGMII ports. + * Temporary modify enet mode to 1G one, so MII functions can + * work correctly. + */ + enet_mode = memac->enet_mode; + memac->enet_mode = + MAKE_ENET_MODE(ENET_INTERFACE_FROM_MODE(memac->enet_mode), + ENET_SPEED_1000); + + /* 1000BaseX mode */ + tmp_reg16 = PHY_SGMII_IF_MODE_1000X; + memac_mii_write_phy_reg(memac, phy_addr, 0x14, tmp_reg16); + + /* AN Device capability */ + tmp_reg16 = PHY_SGMII_DEV_ABILITY_1000X; + memac_mii_write_phy_reg(memac, phy_addr, 0x4, tmp_reg16); + + /* Adjust link timer for SGMII - + * For Serdes 1000BaseX auto-negotiation the timer should be 10 ms. + * The link_timer register is configured in units of the clock. + * - When running as 1G SGMII, Serdes clock is 125 MHz, so + * unit = 1 / (125*10^6 Hz) = 8 ns. + * 10 ms in units of 8 ns = 10ms / 8ns = 1250000 = 0x1312d0 + * - When running as 2.5G SGMII, Serdes clock is 312.5 MHz, so + * unit = 1 / (312.5*10^6 Hz) = 3.2 ns. + * 10 ms in units of 3.2 ns = 10ms / 3.2ns = 3125000 = 0x2faf08. + * Since link_timer value of 1G SGMII will be too short for 2.5 SGMII, + * we always set up here a value of 2.5 SGMII. + */ + memac_mii_write_phy_reg(memac, phy_addr, 0x13, 0x002f); + memac_mii_write_phy_reg(memac, phy_addr, 0x12, 0xaf08); + + /* Restart AN */ + tmp_reg16 = PHY_SGMII_CR_DEF_VAL | PHY_SGMII_CR_RESET_AN; + memac_mii_write_phy_reg(memac, phy_addr, 0x0, tmp_reg16); + + /* Restore original enet mode */ + memac->enet_mode = enet_mode; +} + +static int check_init_parameters(struct memac_t *memac) +{ + if (memac->addr == 0) { + pr_err("Ethernet MAC must have a valid MAC address\n"); + return -EINVAL; + } + if (!memac->exception_cb) { + pr_err("Uninitialized exception handler\n"); + return -EINVAL; + } + if (!memac->event_cb) { + pr_warn("Uninitialize event handler\n"); + return -EINVAL; + } + + /* FM_LEN_CHECK_ERRATA_FMAN_SW002 Errata workaround */ + if (!memac->memac_drv_param->no_length_check_enable) { + pr_err("Length Check!\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fm_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = MEMAC_IMASK_TECC_ER; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = MEMAC_IMASK_RECC_ER; + break; + case FM_MAC_EX_TS_FIFO_ECC_ERR: + bit_mask = MEMAC_IMASK_TSECC_ER; + break; + case FM_MAC_EX_MAGIC_PACKET_INDICATION: + bit_mask = MEMAC_IMASK_MGI; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static void memac_err_exception(void *handle) +{ + struct memac_t *memac = (struct memac_t *)handle; + u32 event, imask; + + event = fman_memac_get_event(memac->regs, 0xffffffff); + imask = fman_memac_get_interrupt_mask(memac->regs); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + fman_memac_ack_event(memac->regs, event); + + if (event & MEMAC_IEVNT_TS_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_TS_FIFO_ECC_ERR); + if (event & MEMAC_IEVNT_TX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & MEMAC_IEVNT_RX_ECC_ER) + memac->exception_cb(memac->dev_id, FM_MAC_EX_10G_RX_ECC_ER); +} + +static void memac_exception(void *handle) +{ + struct memac_t *memac = (struct memac_t *)handle; + u32 event, imask; + + event = fman_memac_get_event(memac->regs, 0xffffffff); + imask = fman_memac_get_interrupt_mask(memac->regs); + + /* Imask include both error and notification/event bits. + * Leaving only error bits enabled by imask. + * The imask error bits are shifted by 16 bits offset from + * their corresponding location in the ievent - hence the >> 16 + */ + event &= ((imask & MEMAC_ALL_ERRS_IMASK) >> 16); + + fman_memac_ack_event(memac->regs, event); + + if (event & MEMAC_IEVNT_MGI) + memac->exception_cb(memac->dev_id, + FM_MAC_EX_MAGIC_PACKET_INDICATION); +} + +static void free_init_resources(struct memac_t *memac) +{ + fm_unregister_intr(memac->fm, FM_MOD_MAC, memac->mac_id, + FM_INTR_TYPE_ERR); + + /* release the driver's group hash table */ + free_hash_table(memac->multicast_addr_hash); + memac->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(memac->unicast_addr_hash); + memac->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct memac_cfg *memac_drv_params) +{ + /* Checks if mEMAC driver parameters were initialized */ + if (!memac_drv_params) + return true; + + return false; +} + +int memac_enable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_enable(memac->regs, + (mode & COMM_MODE_RX), (mode & COMM_MODE_TX)); + + return 0; +} + +int memac_disable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_disable(memac->regs, + (mode & COMM_MODE_RX), (mode & COMM_MODE_TX)); + + return 0; +} + +int memac_set_promiscuous(struct fm_mac_dev *fm_mac_dev, bool new_val) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_set_promiscuous(memac->regs, new_val); + + return 0; +} + +int memac_adjust_link(struct fm_mac_dev *fm_mac_dev, enum ethernet_speed speed) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + int full_duplex = true; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_adjust_link(memac->regs, + (enum enet_interface) + ENET_INTERFACE_FROM_MODE(memac->enet_mode), + (enum enet_speed)speed, full_duplex); + return 0; +} + +int memac_cfg_max_frame_len(struct fm_mac_dev *fm_mac_dev, u16 new_val) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->max_frame_length = new_val; + + return 0; +} + +int memac_cfg_reset_on_init(struct fm_mac_dev *fm_mac_dev, bool enable) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + memac->memac_drv_param->reset_on_init = enable; + + return 0; +} + +int memac_set_tx_pause_frames(struct fm_mac_dev *fm_mac_dev, + u8 __maybe_unused priority, + u16 pause_time, + u16 __maybe_unused thresh_time) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_set_tx_pause_frames(memac->regs, FM_MAC_NO_PFC, + pause_time, 0); + + return 0; +} + +int memac_accept_rx_pause_frames(struct fm_mac_dev *fm_mac_dev, bool en) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_set_rx_ignore_pause_frames(memac->regs, !en); + + return 0; +} + +int memac_modify_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_enet_addr) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + fman_memac_add_addr_in_paddr(memac->regs, (u8 *)(*p_enet_addr), 0); + + return 0; +} + +int memac_add_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + struct eth_hash_entry_t *hash_entry; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &memac->multicast_addr_hash->lsts[hash]); + fman_memac_set_hash_table(memac->regs, + (hash | HASH_CTRL_MCAST_EN)); + + return 0; +} + +int memac_del_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + struct eth_hash_entry_t *hash_entry = NULL; + struct list_head *p_pos; + u32 hash; + u64 addr; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + hash = get_mac_addr_hash_code(addr) & HASH_CTRL_ADDR_MASK; + + list_for_each(p_pos, &memac->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(p_pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&memac->multicast_addr_hash->lsts[hash])) + fman_memac_set_hash_table(memac->regs, + (hash & ~HASH_CTRL_MCAST_EN)); + + return 0; +} + +int memac_set_exception(struct fm_mac_dev *fm_mac_dev, + enum fm_mac_exceptions exception, + bool enable) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + u32 bit_mask = 0; + + if (!is_init_done(memac->memac_drv_param)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + memac->exceptions |= bit_mask; + else + memac->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + fman_memac_set_exception(memac->regs, bit_mask, enable); + + return 0; +} + +int memac_init(struct fm_mac_dev *fm_mac_dev) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + struct memac_cfg *memac_drv_param; + enum enet_interface enet_interface; + enum enet_speed enet_speed; + u8 i, phy_addr; + enet_addr_t eth_addr; + enum fm_mac_type port_type; + bool slow_10g_if = false; + int err; + u32 reg32 = 0; + + if (is_init_done(memac->memac_drv_param)) + return -EINVAL; + + err = check_init_parameters(memac); + if (err) + return err; + + memac_drv_param = memac->memac_drv_param; + + if (memac->fm_rev_info.major_rev == 6 && + memac->fm_rev_info.minor_rev == 4) + slow_10g_if = true; + + port_type = + ((ENET_SPEED_FROM_MODE(memac->enet_mode) < ENET_SPEED_10000) ? + FM_MAC_1G : FM_MAC_10G); + + /* First, reset the MAC if desired. */ + if (memac_drv_param->reset_on_init) { + err = fman_memac_reset(memac->regs); + if (err) { + pr_err("fman_memac_reset() failed\n"); + return err; + } + } + + /* MAC Address */ + MAKE_ENET_ADDR_FROM_UINT64(memac->addr, eth_addr); + fman_memac_add_addr_in_paddr(memac->regs, (u8 *)eth_addr, 0); + + enet_interface = + (enum enet_interface)ENET_INTERFACE_FROM_MODE(memac->enet_mode); + enet_speed = (enum enet_speed)ENET_SPEED_FROM_MODE(memac->enet_mode); + + fman_memac_init(memac->regs, + memac->memac_drv_param, + enet_interface, + enet_speed, + slow_10g_if, + memac->exceptions); + + /* FM_RX_FIFO_CORRUPT_ERRATA_10GMAC_A006320 errata workaround + * Exists only in FMan 6.0 and 6.3. + */ + if ((memac->fm_rev_info.major_rev == 6) && + ((memac->fm_rev_info.minor_rev == 0) || + (memac->fm_rev_info.minor_rev == 3))) { + /* MAC strips CRC from received frames - this workaround + * should decrease the likelihood of bug appearance + */ + reg32 = in_be32(&memac->regs->command_config); + reg32 &= ~CMD_CFG_CRC_FWD; + out_be32(&memac->regs->command_config, reg32); + } + + if (ENET_INTERFACE_FROM_MODE(memac->enet_mode) == ENET_IF_SGMII) { + /* Configure internal SGMII PHY */ + if (memac->enet_mode & ENET_IF_SGMII_BASEX) + setup_sgmii_internal_phy_base_x(memac, PHY_MDIO_ADDR); + else + setup_sgmii_internal_phy(memac, PHY_MDIO_ADDR); + } else if (ENET_INTERFACE_FROM_MODE(memac->enet_mode) == + ENET_IF_QSGMII) { + /* Configure 4 internal SGMII PHYs */ + for (i = 0; i < 4; i++) { + /* QSGMII PHY address occupies 3 upper bits of 5-bit + * phy_address; the lower 2 bits are used to extend + * register address space and access each one of 4 + * ports inside QSGMII. + */ + phy_addr = (u8)((PHY_MDIO_ADDR << 2) | i); + if (memac->enet_mode & ENET_IF_SGMII_BASEX) + setup_sgmii_internal_phy_base_x(memac, + phy_addr); + else + setup_sgmii_internal_phy(memac, phy_addr); + } + } + + /* Max Frame Length */ + err = fm_set_mac_max_frame(memac->fm, port_type, memac->mac_id, + memac_drv_param->max_frame_length); + if (err) { + pr_err("settings Mac max frame length is FAILED\n"); + return err; + } + + memac->multicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->multicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + memac->unicast_addr_hash = alloc_hash_table(HASH_TABLE_SIZE); + if (!memac->unicast_addr_hash) { + free_init_resources(memac); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fm_register_intr(memac->fm, FM_MOD_MAC, memac->mac_id, + FM_INTR_TYPE_ERR, memac_err_exception, memac); + + fm_register_intr(memac->fm, FM_MOD_MAC, memac->mac_id, + FM_INTR_TYPE_NORMAL, memac_exception, memac); + + kfree(memac_drv_param); + memac->memac_drv_param = NULL; + + return 0; +} + +int memac_free(struct fm_mac_dev *fm_mac_dev) +{ + struct memac_t *memac = (struct memac_t *)fm_mac_dev; + + free_init_resources(memac); + + kfree(memac->memac_drv_param); + kfree(memac); + + return 0; +} + +void *memac_config(struct fm_mac_params_t *fm_mac_param) +{ + struct memac_t *memac; + struct memac_cfg *memac_drv_param; + void __iomem *base_addr; + + base_addr = fm_mac_param->base_addr; + /* allocate memory for the m_emac data structure */ + memac = kzalloc(sizeof(*memac), GFP_KERNEL); + if (!memac) + return NULL; + + /* allocate memory for the m_emac driver parameters data structure */ + memac_drv_param = kzalloc(sizeof(*memac_drv_param), GFP_KERNEL); + if (!memac_drv_param) { + memac_free((struct fm_mac_dev *)memac); + return NULL; + } + + /* Plant parameter structure pointer */ + memac->memac_drv_param = memac_drv_param; + + fman_memac_defconfig(memac_drv_param); + + memac->addr = ENET_ADDR_TO_UINT64(fm_mac_param->addr); + + memac->regs = (struct memac_regs __iomem *)(base_addr); + memac->mii_regs = (struct memac_mii_access_mem_map __iomem *) + (base_addr + MEMAC_TO_MII_OFFSET); + memac->enet_mode = fm_mac_param->enet_mode; + memac->mac_id = fm_mac_param->mac_id; + memac->exceptions = MEMAC_DEFAULT_EXCEPTIONS; + memac->exception_cb = fm_mac_param->exception_cb; + memac->event_cb = fm_mac_param->event_cb; + memac->dev_id = fm_mac_param->dev_id; + memac->fm = fm_mac_param->fm; + + /* Save FMan revision */ + fm_get_revision(memac->fm, &memac->fm_rev_info); + + return memac; +} diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_memac.h b/drivers/net/ethernet/freescale/fman/mac/fm_memac.h new file mode 100644 index 0000000..1913250 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_memac.h @@ -0,0 +1,115 @@ +/* + * Copyright 2008-2015 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 __MEMAC_H +#define __MEMAC_H + +#include "fsl_fman_memac_mii_acc.h" +#include "fm_mac.h" +#include "fsl_fman_memac.h" + +#define MEMAC_DEFAULT_EXCEPTIONS \ + ((u32)(MEMAC_IMASK_TSECC_ER | MEMAC_IMASK_TECC_ER | \ + MEMAC_IMASK_RECC_ER | MEMAC_IMASK_MGI)) + +struct memac_t { + /* Pointer to MAC memory mapped registers */ + struct memac_regs __iomem *regs; + /* Pointer to MII memory mapped registers */ + struct memac_mii_access_mem_map __iomem *mii_regs; + /* MAC address of device */ + u64 addr; + /* Ethernet physical interface */ + enum e_enet_mode enet_mode; + void *dev_id; /* device cookie used by the exception cbs */ + fm_mac_exception_cb *exception_cb; + fm_mac_exception_cb *event_cb; + /* Whether a particular individual address + * recognition register is being used + */ + bool ind_addr_reg_used[MEMAC_NUM_OF_PADDRS]; + /* MAC address for particular individual address + * recognition register + */ + u64 paddr[MEMAC_NUM_OF_PADDRS]; + /* Number of individual addresses in registers for this station. */ + u8 num_of_ind_addr_in_regs; + /* Pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* Pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + bool debug_mode; + u8 mac_id; + u32 exceptions; + struct memac_cfg *memac_drv_param; + void *fm; + struct fm_revision_info_t fm_rev_info; +}; + +/* Internal PHY access */ +#define PHY_MDIO_ADDR 0 + +/* Internal PHY Registers - SGMII */ +#define PHY_SGMII_CR_RESET_AN 0x0200 +#define PHY_SGMII_CR_DEF_VAL 0x1140 +#define PHY_SGMII_DEV_ABILITY_SGMII 0x4001 +#define PHY_SGMII_DEV_ABILITY_1000X 0x01A0 +#define PHY_SGMII_IF_MODE_AN 0x0002 +#define PHY_SGMII_IF_MODE_SGMII 0x0001 +#define PHY_SGMII_IF_MODE_1000X 0x0000 + +/* Offset from the MEM map to the MDIO mem map */ +#define MEMAC_TO_MII_OFFSET 0x030 + +void *memac_config(struct fm_mac_params_t *p_fm_mac_param); +int memac_set_promiscuous(struct fm_mac_dev *fm_mac_dev, bool new_val); +int memac_modify_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_enet_addr); +int memac_adjust_link(struct fm_mac_dev *fm_mac_dev, + enum ethernet_speed speed); +int memac_cfg_max_frame_len(struct fm_mac_dev *fm_mac_dev, u16 new_val); +int memac_cfg_reset_on_init(struct fm_mac_dev *fm_mac_dev, bool enable); +int memac_enable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode); +int memac_disable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode); +int memac_init(struct fm_mac_dev *fm_mac_dev); +int memac_free(struct fm_mac_dev *fm_mac_dev); +int memac_accept_rx_pause_frames(struct fm_mac_dev *fm_mac_dev, bool en); +int memac_set_tx_pause_frames(struct fm_mac_dev *fm_mac_dev, u8 priority, + u16 pause_time, u16 thresh_time); +int memac_set_exception(struct fm_mac_dev *fm_mac_dev, + enum fm_mac_exceptions exception, bool enable); +int memac_add_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_eth_addr); +int memac_del_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_eth_addr); + +#endif /* __MEMAC_H */ diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_tgec.c b/drivers/net/ethernet/freescale/fman/mac/fm_tgec.c new file mode 100644 index 0000000..3ba0608 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_tgec.c @@ -0,0 +1,552 @@ +/* + * Copyright 2008-2015 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. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include "crc_mac_addr_ext.h" + +#include "fm_common.h" +#include "fsl_fman_tgec.h" +#include "fm_tgec.h" + +#include +#include +#include + +static int check_init_parameters(struct tgec_t *tgec) +{ + if (ENET_SPEED_FROM_MODE(tgec->enet_mode) < ENET_SPEED_10000) { + pr_err("10G MAC driver only support 10G speed\n"); + return -EINVAL; + } + if (tgec->addr == 0) { + pr_err("Ethernet 10G MAC Must have valid MAC Address\n"); + return -EINVAL; + } + if (!tgec->exception_cb) { + pr_err("uninitialized exception_cb\n"); + return -EINVAL; + } + if (!tgec->event_cb) { + pr_err("uninitialized event_cb\n"); + return -EINVAL; + } + + /* FM_LEN_CHECK_ERRATA_FMAN_SW002 Errata workaround */ + if (!tgec->tgec_drv_param->no_length_check_enable) { + pr_warn("Length Check!\n"); + return -EINVAL; + } + + return 0; +} + +static int get_exception_flag(enum fm_mac_exceptions exception) +{ + u32 bit_mask; + + switch (exception) { + case FM_MAC_EX_10G_MDIO_SCAN_EVENT: + bit_mask = TGEC_IMASK_MDIO_SCAN_EVENT; + break; + case FM_MAC_EX_10G_MDIO_CMD_CMPL: + bit_mask = TGEC_IMASK_MDIO_CMD_CMPL; + break; + case FM_MAC_EX_10G_REM_FAULT: + bit_mask = TGEC_IMASK_REM_FAULT; + break; + case FM_MAC_EX_10G_LOC_FAULT: + bit_mask = TGEC_IMASK_LOC_FAULT; + break; + case FM_MAC_EX_10G_TX_ECC_ER: + bit_mask = TGEC_IMASK_TX_ECC_ER; + break; + case FM_MAC_EX_10G_TX_FIFO_UNFL: + bit_mask = TGEC_IMASK_TX_FIFO_UNFL; + break; + case FM_MAC_EX_10G_TX_FIFO_OVFL: + bit_mask = TGEC_IMASK_TX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_TX_ER: + bit_mask = TGEC_IMASK_TX_ER; + break; + case FM_MAC_EX_10G_RX_FIFO_OVFL: + bit_mask = TGEC_IMASK_RX_FIFO_OVFL; + break; + case FM_MAC_EX_10G_RX_ECC_ER: + bit_mask = TGEC_IMASK_RX_ECC_ER; + break; + case FM_MAC_EX_10G_RX_JAB_FRM: + bit_mask = TGEC_IMASK_RX_JAB_FRM; + break; + case FM_MAC_EX_10G_RX_OVRSZ_FRM: + bit_mask = TGEC_IMASK_RX_OVRSZ_FRM; + break; + case FM_MAC_EX_10G_RX_RUNT_FRM: + bit_mask = TGEC_IMASK_RX_RUNT_FRM; + break; + case FM_MAC_EX_10G_RX_FRAG_FRM: + bit_mask = TGEC_IMASK_RX_FRAG_FRM; + break; + case FM_MAC_EX_10G_RX_LEN_ER: + bit_mask = TGEC_IMASK_RX_LEN_ER; + break; + case FM_MAC_EX_10G_RX_CRC_ER: + bit_mask = TGEC_IMASK_RX_CRC_ER; + break; + case FM_MAC_EX_10G_RX_ALIGN_ER: + bit_mask = TGEC_IMASK_RX_ALIGN_ER; + break; + default: + bit_mask = 0; + break; + } + + return bit_mask; +} + +static u32 get_mac_addr_hash_code(u64 eth_addr) +{ + u32 crc; + + /* CRC calculation */ + GET_MAC_ADDR_CRC(eth_addr, crc); + + crc = bitrev32(crc); + + return crc; +} + +static void tgec_err_exception(void *handle) +{ + struct tgec_t *tgec = (struct tgec_t *)handle; + u32 event; + struct tgec_regs __iomem *regs = tgec->regs; + + /* do not handle MDIO events */ + event = fman_tgec_get_event(regs, + ~(TGEC_IMASK_MDIO_SCAN_EVENT | + TGEC_IMASK_MDIO_CMD_CMPL)); + event &= fman_tgec_get_interrupt_mask(regs); + + fman_tgec_ack_event(regs, event); + + if (event & TGEC_IMASK_REM_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_REM_FAULT); + if (event & TGEC_IMASK_LOC_FAULT) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_LOC_FAULT); + if (event & TGEC_IMASK_TX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ECC_ER); + if (event & TGEC_IMASK_TX_FIFO_UNFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_UNFL); + if (event & TGEC_IMASK_TX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_FIFO_OVFL); + if (event & TGEC_IMASK_TX_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_TX_ER); + if (event & TGEC_IMASK_RX_FIFO_OVFL) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FIFO_OVFL); + if (event & TGEC_IMASK_RX_ECC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ECC_ER); + if (event & TGEC_IMASK_RX_JAB_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_JAB_FRM); + if (event & TGEC_IMASK_RX_OVRSZ_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_OVRSZ_FRM); + if (event & TGEC_IMASK_RX_RUNT_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_RUNT_FRM); + if (event & TGEC_IMASK_RX_FRAG_FRM) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_FRAG_FRM); + if (event & TGEC_IMASK_RX_LEN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_LEN_ER); + if (event & TGEC_IMASK_RX_CRC_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_CRC_ER); + if (event & TGEC_IMASK_RX_ALIGN_ER) + tgec->exception_cb(tgec->dev_id, FM_MAC_EX_10G_RX_ALIGN_ER); +} + +static void free_init_resources(struct tgec_t *tgec) +{ + fm_unregister_intr(tgec->fm, FM_MOD_MAC, tgec->mac_id, + FM_INTR_TYPE_ERR); + + /* release the driver's group hash table */ + free_hash_table(tgec->multicast_addr_hash); + tgec->multicast_addr_hash = NULL; + + /* release the driver's individual hash table */ + free_hash_table(tgec->unicast_addr_hash); + tgec->unicast_addr_hash = NULL; +} + +static bool is_init_done(struct tgec_cfg *tgec_drv_parameters) +{ + /* Checks if tGEC driver parameters were initialized */ + if (!tgec_drv_parameters) + return true; + + return false; +} + +int tgec_enable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + fman_tgec_enable(tgec->regs, + (mode & COMM_MODE_RX), (mode & COMM_MODE_TX)); + + return 0; +} + +int tgec_disable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + fman_tgec_disable(tgec->regs, + (mode & COMM_MODE_RX), (mode & COMM_MODE_TX)); + + return 0; +} + +int tgec_set_promiscuous(struct fm_mac_dev *fm_mac_dev, bool new_val) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + fman_tgec_set_promiscuous(tgec->regs, new_val); + + return 0; +} + +int tgec_cfg_max_frame_len(struct fm_mac_dev *fm_mac_dev, u16 new_val) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + tgec->tgec_drv_param->max_frame_length = new_val; + + return 0; +} + +int tgec_set_tx_pause_frames(struct fm_mac_dev *fm_mac_dev, + u8 __maybe_unused priority, + u16 pause_time, + u16 __maybe_unused thresh_time) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + fman_tgec_set_tx_pause_frames(tgec->regs, pause_time); + + return 0; +} + +int tgec_accept_rx_pause_frames(struct fm_mac_dev *fm_mac_dev, bool en) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + fman_tgec_set_rx_ignore_pause_frames(tgec->regs, !en); + + return 0; +} + +int tgec_modify_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_enet_addr) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + tgec->addr = ENET_ADDR_TO_UINT64(*p_enet_addr); + fman_tgec_set_mac_address(tgec->regs, (u8 *)(*p_enet_addr)); + + return 0; +} + +int tgec_add_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + struct eth_hash_entry_t *hash_entry; + u32 crc; + u32 hash; + u64 addr; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + addr = ENET_ADDR_TO_UINT64(*eth_addr); + + if (!(addr & GROUP_ADDRESS)) { + /* Unicast addresses not supported in hash */ + pr_err("Unicast Address\n"); + return -EINVAL; + } + /* CRC calculation */ + crc = get_mac_addr_hash_code(addr); + + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + /* Create element to be added to the driver hash table */ + hash_entry = kmalloc(sizeof(*hash_entry), GFP_KERNEL); + if (!hash_entry) + return -ENOMEM; + hash_entry->addr = addr; + INIT_LIST_HEAD(&hash_entry->node); + + list_add_tail(&hash_entry->node, + &tgec->multicast_addr_hash->lsts[hash]); + fman_tgec_set_hash_table(tgec->regs, (hash | TGEC_HASH_MCAST_EN)); + + return 0; +} + +int tgec_del_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *eth_addr) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + struct eth_hash_entry_t *hash_entry = NULL; + struct list_head *p_pos; + u32 crc; + u32 hash; + u64 addr; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + addr = ((*(u64 *)eth_addr) >> 16); + + /* CRC calculation */ + crc = get_mac_addr_hash_code(addr); + /* Take 9 MSB bits */ + hash = (crc >> TGEC_HASH_MCAST_SHIFT) & TGEC_HASH_ADR_MSK; + + list_for_each(p_pos, &tgec->multicast_addr_hash->lsts[hash]) { + hash_entry = ETH_HASH_ENTRY_OBJ(p_pos); + if (hash_entry->addr == addr) { + list_del_init(&hash_entry->node); + kfree(hash_entry); + break; + } + } + if (list_empty(&tgec->multicast_addr_hash->lsts[hash])) + fman_tgec_set_hash_table(tgec->regs, + (hash & ~TGEC_HASH_MCAST_EN)); + + return 0; +} + +int tgec_get_version(struct fm_mac_dev *fm_mac_dev, u32 *mac_version) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + *mac_version = fman_tgec_get_revision(tgec->regs); + + return 0; +} + +int tgec_set_exception(struct fm_mac_dev *fm_mac_dev, + enum fm_mac_exceptions exception, + bool enable) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + u32 bit_mask = 0; + + if (!is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + bit_mask = get_exception_flag(exception); + if (bit_mask) { + if (enable) + tgec->exceptions |= bit_mask; + else + tgec->exceptions &= ~bit_mask; + } else { + pr_err("Undefined exception\n"); + return -EINVAL; + } + if (enable) + fman_tgec_enable_interrupt(tgec->regs, bit_mask); + else + fman_tgec_disable_interrupt(tgec->regs, bit_mask); + + return 0; +} + +int tgec_init(struct fm_mac_dev *fm_mac_dev) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + struct tgec_cfg *tgec_drv_param; + enet_addr_t eth_addr; + int err; + + if (is_init_done(tgec->tgec_drv_param)) + return -EINVAL; + + if (DEFAULT_RESET_ON_INIT && + (fm_reset_mac(tgec->fm, tgec->mac_id) != 0)) { + pr_err("Can't reset MAC!\n"); + return -EINVAL; + } + + fm_get_revision(tgec->fm, &tgec->fm_rev_info); + err = check_init_parameters(tgec); + if (err) + return err; + + tgec_drv_param = tgec->tgec_drv_param; + + MAKE_ENET_ADDR_FROM_UINT64(tgec->addr, eth_addr); + fman_tgec_set_mac_address(tgec->regs, (u8 *)eth_addr); + + /* interrupts */ + /* FM_10G_REM_N_LCL_FLT_EX_10GMAC_ERRATA_SW005 Errata workaround */ + if (tgec->fm_rev_info.major_rev <= 2) + tgec->exceptions &= ~(TGEC_IMASK_REM_FAULT | + TGEC_IMASK_LOC_FAULT); + + err = fman_tgec_init(tgec->regs, tgec_drv_param, tgec->exceptions); + if (err) { + free_init_resources(tgec); + pr_err("TGEC version doesn't support this i/f mode\n"); + return err; + } + + /* Max Frame Length */ + err = fm_set_mac_max_frame(tgec->fm, FM_MAC_10G, tgec->mac_id, + tgec_drv_param->max_frame_length); + if (err) { + pr_err("Setting max frame length FAILED\n"); + free_init_resources(tgec); + return -EINVAL; + } + + /* FM_TX_FIFO_CORRUPTION_ERRATA_10GMAC_A007 Errata workaround */ + if (tgec->fm_rev_info.major_rev == 2) + fman_tgec_set_erratum_tx_fifo_corruption_10gmac_a007(tgec-> + regs); + + tgec->multicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->multicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + tgec->unicast_addr_hash = alloc_hash_table(TGEC_HASH_TABLE_SIZE); + if (!tgec->unicast_addr_hash) { + free_init_resources(tgec); + pr_err("allocation hash table is FAILED\n"); + return -ENOMEM; + } + + fm_register_intr(tgec->fm, FM_MOD_MAC, tgec->mac_id, + FM_INTR_TYPE_ERR, tgec_err_exception, tgec); + + kfree(tgec_drv_param); + tgec->tgec_drv_param = NULL; + + return 0; +} + +int tgec_free(struct fm_mac_dev *fm_mac_dev) +{ + struct tgec_t *tgec = (struct tgec_t *)fm_mac_dev; + + free_init_resources(tgec); + + if (tgec->tgec_drv_param) + tgec->tgec_drv_param = NULL; + + kfree(tgec->tgec_drv_param); + kfree(tgec); + + return 0; +} + +void *tgec_config(struct fm_mac_params_t *fm_mac_param) +{ + struct tgec_t *tgec; + struct tgec_cfg *tgec_drv_param; + void __iomem *base_addr; + + base_addr = fm_mac_param->base_addr; + /* allocate memory for the UCC GETH data structure. */ + tgec = kzalloc(sizeof(*tgec), GFP_KERNEL); + if (!tgec) + return NULL; + + /* allocate memory for the 10G MAC driver parameters data structure. */ + tgec_drv_param = kzalloc(sizeof(*tgec_drv_param), GFP_KERNEL); + if (!tgec_drv_param) { + tgec_free((struct fm_mac_dev *)tgec); + return NULL; + } + + /* Plant parameter structure pointer */ + tgec->tgec_drv_param = tgec_drv_param; + + fman_tgec_defconfig(tgec_drv_param); + + tgec->regs = (struct tgec_regs __iomem *)(base_addr); + tgec->addr = ENET_ADDR_TO_UINT64(fm_mac_param->addr); + tgec->enet_mode = fm_mac_param->enet_mode; + tgec->mac_id = fm_mac_param->mac_id; + tgec->exceptions = TGEC_DEFAULT_EXCEPTIONS; + tgec->exception_cb = fm_mac_param->exception_cb; + tgec->event_cb = fm_mac_param->event_cb; + tgec->dev_id = fm_mac_param->dev_id; + tgec->fm = fm_mac_param->fm; + + /* Save FMan revision */ + fm_get_revision(tgec->fm, &tgec->fm_rev_info); + + return tgec; +} diff --git a/drivers/net/ethernet/freescale/fman/mac/fm_tgec.h b/drivers/net/ethernet/freescale/fman/mac/fm_tgec.h new file mode 100644 index 0000000..764a465 --- /dev/null +++ b/drivers/net/ethernet/freescale/fman/mac/fm_tgec.h @@ -0,0 +1,124 @@ +/* + * Copyright 2008-2015 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 __TGEC_H +#define __TGEC_H + +#include "enet_ext.h" + +#include "fm_mac.h" + +#define TGEC_DEFAULT_EXCEPTIONS \ + ((u32)((TGEC_IMASK_MDIO_SCAN_EVENT) |\ + (TGEC_IMASK_REM_FAULT) |\ + (TGEC_IMASK_LOC_FAULT) |\ + (TGEC_IMASK_TX_ECC_ER) |\ + (TGEC_IMASK_TX_FIFO_UNFL) |\ + (TGEC_IMASK_TX_FIFO_OVFL) |\ + (TGEC_IMASK_TX_ER) |\ + (TGEC_IMASK_RX_FIFO_OVFL) |\ + (TGEC_IMASK_RX_ECC_ER) |\ + (TGEC_IMASK_RX_JAB_FRM) |\ + (TGEC_IMASK_RX_OVRSZ_FRM) |\ + (TGEC_IMASK_RX_RUNT_FRM) |\ + (TGEC_IMASK_RX_FRAG_FRM) |\ + (TGEC_IMASK_RX_CRC_ER) |\ + (TGEC_IMASK_RX_ALIGN_ER))) + +#define MAX_PACKET_ALIGNMENT 31 +#define MAX_INTER_PACKET_GAP 0x7f +#define MAX_INTER_PALTERNATE_BEB 0x0f +#define MAX_RETRANSMISSION 0x0f +#define MAX_COLLISION_WINDOW 0x03ff + +/* number of pattern match registers (entries) */ +#define TGEC_NUM_OF_PADDRS 1 + +/* Group address bit indication */ +#define GROUP_ADDRESS 0x0000010000000000LL + +/* Hash table size (= 32 bits*8 regs) */ +#define TGEC_HASH_TABLE_SIZE 512 + +struct tgec_t { + /* pointer to 10G memory mapped registers. */ + struct tgec_regs __iomem *regs; + /* MAC address of device; */ + u64 addr; + /* Ethernet physical interface */ + enum e_enet_mode enet_mode; + void *dev_id; /* device cookie used by the exception cbs */ + fm_mac_exception_cb *exception_cb; + fm_mac_exception_cb *event_cb; + /* Whether a particular individual address recognition + * register is being used + */ + bool ind_addr_reg_used[TGEC_NUM_OF_PADDRS]; + /* MAC address for particular individual address + * recognition register + */ + u64 paddr[TGEC_NUM_OF_PADDRS]; + /* Number of individual addresses in registers for this station. */ + u8 num_of_ind_addr_in_regs; + /* pointer to driver's global address hash table */ + struct eth_hash_t *multicast_addr_hash; + /* pointer to driver's individual address hash table */ + struct eth_hash_t *unicast_addr_hash; + bool debug_mode; + u8 mac_id; + u32 exceptions; + struct tgec_cfg *tgec_drv_param; + void *fm; + struct fm_revision_info_t fm_rev_info; +}; + +void *tgec_config(struct fm_mac_params_t *p_fm_mac_params); +int tgec_set_promiscuous(struct fm_mac_dev *fm_mac_dev, bool new_val); +int tgec_modify_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_enet_addr); +int tgec_cfg_max_frame_len(struct fm_mac_dev *fm_mac_dev, u16 new_val); +int tgec_enable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode); +int tgec_disable(struct fm_mac_dev *fm_mac_dev, enum comm_mode mode); +int tgec_init(struct fm_mac_dev *fm_mac_dev); +int tgec_free(struct fm_mac_dev *fm_mac_dev); +int tgec_accept_rx_pause_frames(struct fm_mac_dev *fm_mac_dev, bool en); +int tgec_set_tx_pause_frames(struct fm_mac_dev *fm_mac_dev, u8 priority, + u16 pause_time, u16 thresh_time); +int tgec_set_exception(struct fm_mac_dev *fm_mac_dev, + enum fm_mac_exceptions exception, bool enable); +int tgec_add_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_eth_addr); +int tgec_del_hash_mac_address(struct fm_mac_dev *fm_mac_dev, + enet_addr_t *p_eth_addr); +int tgec_get_version(struct fm_mac_dev *fm_mac_dev, u32 *mac_version); + +#endif /* __TGEC_H */ -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/