From 0d4c44167f6ce1e7a9e2d7e0a1b67b93456328ca Mon Sep 17 00:00:00 2001 From: Badreddine ZAGROUBA Date: Fri, 1 Oct 2010 17:36:35 +0200 Subject: cleo/linux/eth : Add IP175x swich driver handel VLAN managed by ethtool, fix #398 *Add ethtool patch for VLAN configuration *Add VLAN management code to IP175x & ETH drivers --- .../drivers/net/arm/synop3504.c | 26 +- .../drivers/net/phy/icplus.c | 367 ++++++++++++++++++--- .../drivers/net/phy/icplus.h | 91 +++++ 3 files changed, 438 insertions(+), 46 deletions(-) create mode 100644 cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.h (limited to 'cleopatre/linux-2.6.25.10-spc300/drivers/net') diff --git a/cleopatre/linux-2.6.25.10-spc300/drivers/net/arm/synop3504.c b/cleopatre/linux-2.6.25.10-spc300/drivers/net/arm/synop3504.c index 89c997844f..53a25c1326 100644 --- a/cleopatre/linux-2.6.25.10-spc300/drivers/net/arm/synop3504.c +++ b/cleopatre/linux-2.6.25.10-spc300/drivers/net/arm/synop3504.c @@ -27,7 +27,7 @@ */ #define DRV_NAME "Synop3504" -#define DRV_VERSION "3.1" +#define DRV_VERSION "3.2" #define DRV_RELDATE "nov 17, 2009" //#define TRACE_FRAME 1 @@ -646,6 +646,30 @@ static int synop3504_ethtool_ioctl(struct net_device *dev, void *useraddr) return -EFAULT; return 0; } + case ETHTOOL_RVLAN_TABLE: + { + struct ethtool_vlanparam ecmd; + + if (priv->phydev->drv->read_vlan_table (priv->phydev, &ecmd)) + return -EFAULT; + + if(copy_to_user(useraddr, &ecmd, sizeof(ecmd))) + return -EFAULT; + + return 0; + } + case ETHTOOL_SVLAN_TABLE: + { + struct ethtool_vlanparam ecmd; + + if(copy_from_user(&ecmd, useraddr, sizeof(ecmd))) + return -EFAULT; + + if (priv->phydev->drv->write_vlan_table (priv->phydev, &ecmd)) + return -EFAULT; + + return 0; + } default: break; } diff --git a/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c b/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c index be4c3387f5..b7feecd0a2 100644 --- a/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c +++ b/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c @@ -1,14 +1,23 @@ -/* - * Driver for ICPlus PHYs +/* Cleopatre project {{{ * - * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (C) 2008 SPiDCOM Technologies * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. * - */ + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * }}} */ #include #include #include @@ -31,44 +40,309 @@ #include #include -MODULE_DESCRIPTION("ICPlus IP175C PHY driver"); -MODULE_AUTHOR("Michael Barkowski"); +#include "icplus.h" + +//#define TRACE(...) printk(DRV_NAME": " __VA_ARGS__) +#define TRACE(...) + +MODULE_DESCRIPTION("ICPlus ip175C/D PHY driver"); +MODULE_AUTHOR("SPiDCOM Technologies"); MODULE_LICENSE("GPL"); -static int ip175c_config_init(struct phy_device *phydev) + +/** Private structure for our phy device */ +struct ip175x_priv +{ + struct phy_device *phydev; + uint32_t model; + uint16_t vlan_valid; +}; + + +int ip175x_read_vlan_table (struct phy_device *phydev, struct ethtool_vlanparam *ecmd) +{ + struct ip175x_priv *priv = phydev->priv; + u16 reg; + int port; + + switch(priv->model) + { + case ICPLUS_MODEL_IP175D: + // Read VLAN_MODE[5:0] field in PHY22, MII0. + reg = phydev->bus->read(phydev->bus, + IP175D_VLAN_CLS_REG_PHY, + IP175D_VLAN_CLS_REG_NUM); + TRACE ("IP175D_VLAN_CLS_REG (%d:%d)= %x\n",IP175D_VLAN_CLS_REG_PHY, IP175D_VLAN_CLS_REG_NUM, reg); + for (port = 0; port < MAX_VLAN_SUPPORTED; port++) + { + ecmd->vlan_table[port].state = (reg & (1 << port)) != 0 ? VLAN_ON : VLAN_OFF; + ecmd->vlan_table[port].vlan = phydev->bus->read(phydev->bus, + IP175D_VLAN_INFO_REG_PHY, + IP175D_VLAN_INFO_REG_NUM(port)); + TRACE("IP175D_VLAN_INFO_REG (%d:%d) =%x\n", IP175D_VLAN_INFO_REG_PHY, IP175D_VLAN_INFO_REG_NUM(port), ecmd->vlan_table[port].vlan); + } + break; + + default : + // Feature not supported + return -EOPNOTSUPP; + + } + + return 0; +} + +int ip175x_write_vlan_table (struct phy_device *phydev, struct ethtool_vlanparam *ecmd) +{ + struct ip175x_priv *priv = phydev->priv; + uint8_t add_tag[IP175D_VLAN_TABLE_SIZE]; + uint8_t remove_tag[IP175D_VLAN_TABLE_SIZE]; + uint8_t vlan_member[IP175D_VLAN_TABLE_SIZE]; + + memset(add_tag, 0, sizeof(add_tag)); + memset(remove_tag, 0, sizeof(remove_tag)); + memset(vlan_member, 0, sizeof(vlan_member)); + + switch(priv->model) + { + case ICPLUS_MODEL_IP175D: + { + int i, j; + + // entry from 0 to 4 are reserved for port-based or tag-based. + for (i=0; i < MAX_VLAN_SUPPORTED; i++) + { + // Write VLAN_INFO_x ( PVID ) + phydev->bus->write( phydev->bus, + IP175D_VLAN_INFO_REG_PHY, + IP175D_VLAN_INFO_REG_NUM(i), + (ecmd->vlan_table[i].vlan & 0xFFF)); + TRACE("IP175D_VLAN_INFO_REG w%d:%d=%x\n", IP175D_VLAN_INFO_REG_PHY, IP175D_VLAN_INFO_REG_NUM(i), (ecmd->vlan_table[i].vlan & 0xFFF)); + + // Write the VID_x + phydev->bus->write( phydev->bus, + IP175D_VLAN_ID_REG_PHY, + IP175D_VLAN_ID_REG_NUM(i), + (ecmd->vlan_table[i].vlan & 0xFFF)); + TRACE("IP175D_VLAN_ID_REG w%d:%d=%x\n", IP175D_VLAN_ID_REG_PHY, IP175D_VLAN_ID_REG_NUM(i), (ecmd->vlan_table[i].vlan & 0xFFF)); + + if (ecmd->vlan_table[i].state == VLAN_ON) + SETBIT(priv->vlan_valid,i); + else + CLEARBIT(priv->vlan_valid,i); + } + + // entry 5, is connected to spc300. special entry. always in tag mode. + SETBIT(priv->vlan_valid,IP175D_VLAN_TABLE_COMMON_PHY); + vlan_member[IP175D_VLAN_TABLE_COMMON_PHY] = 0x3F; // Maybe this line is not usefull. + phydev->bus->write(phydev->bus, + IP175D_VLAN_INFO_REG_PHY, + IP175D_VLAN_INFO_REG_NUM(IP175D_VLAN_TABLE_COMMON_PHY), + 0); // PVID_5 = 0 + + // entry 6. + SETBIT(priv->vlan_valid,IP175D_VLAN_TABLE_NO_TAG_LINE); + SETBIT(vlan_member[IP175D_VLAN_TABLE_NO_TAG_LINE],IP175D_VLAN_TABLE_COMMON_PHY); + phydev->bus->write(phydev->bus, + IP175D_VLAN_ID_REG_PHY, + IP175D_VLAN_ID_REG_NUM(IP175D_VLAN_TABLE_NO_TAG_LINE), + 0); // Write the PVID_6 (= PVID_5) + + for (i=0; i < MAX_VLAN_SUPPORTED; i++) + { + if (ecmd->vlan_table[i].state == VLAN_ON) + { + SETBIT(add_tag[i],IP175D_VLAN_TABLE_COMMON_PHY); // Add tag from outside to spc300. + remove_tag[i] = 0x1F; // Remove tag from spc300 to outside and from other port also. + SETBIT(vlan_member[i],IP175D_VLAN_TABLE_COMMON_PHY); + SETBIT(vlan_member[i],i); + + for (j=i+1; j < MAX_VLAN_SUPPORTED; j++) + { + // The same VID can forward the packet between them. + if ((ecmd->vlan_table[i].vlan == ecmd->vlan_table[j].vlan) && + (ecmd->vlan_table[j].state == VLAN_ON) + ) + { + SETBIT(vlan_member[i],j); + SETBIT(vlan_member[j],i); + } + } + } + else + { + // The no-VID can forward the packet between them. + SETBIT(vlan_member[IP175D_VLAN_TABLE_NO_TAG_LINE], i); + } + } + + for (i=0; i < MAX_VLAN_SUPPORTED; i++) + { + if (ecmd->vlan_table[i].state == VLAN_OFF) + { + // The no-VID have the same forward rules. + vlan_member[i] = vlan_member[IP175D_VLAN_TABLE_NO_TAG_LINE]; + } + } + + for (i=0; i < IP175D_VLAN_TABLE_SIZE; i+=2) + { + uint16_t reg; + + reg = ((vlan_member[i+1] << 8) & 0xFF00) + (vlan_member[i+0] & 0x00FF); + phydev->bus->write( phydev->bus, + IP175D_VLAN_MEMBER_REG_PHY, + IP175D_VLAN_MEMBER_REG_NUM(i), + reg); // VLAN_MEMBER + + reg = ((add_tag[i+1] << 8) & 0xFF00) + (add_tag[i+0] & 0x00FF); + phydev->bus->write( phydev->bus, + IP175D_ADD_TAG_REG_PHY, + IP175D_ADD_TAG_REG_NUM(i), + reg); // ADD_TAG + + reg = ((remove_tag[i+1] << 8) & 0xFF00) + (remove_tag[i+0] & 0x00FF); + phydev->bus->write( phydev->bus, + IP175D_REMOVE_TAG_REG_PHY, + IP175D_REMOVE_TAG_REG_NUM(i), + reg); // REMOVE_TAG + } + + // Global register + phydev->bus->write(phydev->bus, + IP175D_VLAN_CLS_REG_PHY, + IP175D_VLAN_CLS_REG_NUM, + UNVID_MODE_FORWARD_TO_CPU | (priv->vlan_valid & 0x3F)); // Write the VLAN_Mode (TAGGED BASED) + + phydev->bus->write(phydev->bus, + IP175D_VLAN_VALID_REG_PHY, + IP175D_VLAN_VALID_REG_NUM, + priv->vlan_valid); // Write the VLAN_VALID + } + break; + + default : + // Feature not supported + return -EOPNOTSUPP; + } + return 0; +} + + +static int ip175x_get_model(struct phy_device *phydev) +{ + struct ip175x_priv *priv; + priv = (struct ip175x_priv *)phydev->priv; + + if (!phydev) + { + printk (KERN_ERR "%s: no PHY found\n", DRV_NAME); + return -EINVAL; + } + + if(0x175D == phydev->bus->read(phydev->bus, 20, 0)) + { + priv->model = ICPLUS_MODEL_IP175D; + TRACE("PHY model is IC+ IP175D \n"); + } + else + { + priv->model = ICPLUS_MODEL_IP175C; + TRACE("PHY model is IC+ ip175C \n"); + } + return 0; +} + +static int ip175x_probe(struct phy_device *phydev) +{ + struct ip175x_priv *priv; + int err; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + phydev->priv = priv; + priv->phydev = phydev; + + err = ip175x_get_model(phydev); + if (err < 0) + goto error; + + return 0; + +error: + kfree(priv); + return err; +} + + +static int ip175x_config_init(struct phy_device *phydev) { int err, i; + struct ip175x_priv *priv; static int full_reset_performed = 0; - printk("ICPlus IP175C : Driver Initialization - PHY addr = %d\n", phydev->addr); + priv = (struct ip175x_priv *)phydev->priv; + + printk("ICPlus ip175x : Driver Initialization - PHY addr = %d\n", phydev->addr); if (full_reset_performed == 0) { - /* master reset */ - err = phydev->bus->write(phydev->bus, 30, 0, 0x175c); - if (err < 0) - return err; + if (priv->model == ICPLUS_MODEL_IP175C){ + /* master reset */ + err = phydev->bus->write(phydev->bus, 30, 0, IP175C_RESET_VAL); + if (err < 0) + return err; + + /* ensure no bus delays overlap reset period */ + err = phydev->bus->read(phydev->bus, 30, 0); + if (err < 0) + return err; - /* ensure no bus delays overlap reset period */ - err = phydev->bus->read(phydev->bus, 30, 0); + /* data sheet specifies reset period is 2 msec */ + mdelay(2); - /* data sheet specifies reset period is 2 msec */ - mdelay(2); + /* enable ip175x mode */ + err = phydev->bus->write(phydev->bus, 29, 31, IP175C_RESET_VAL); + if (err < 0) + return err; - /* enable IP175C mode */ - err = phydev->bus->write(phydev->bus, 29, 31, 0x175c); - if (err < 0) - return err; + /* Set MII0 speed and duplex (in PHY mode) */ + err = phydev->bus->write(phydev->bus, 29, 22, 0x420); + if (err < 0) + return err; + } + else if (priv->model == ICPLUS_MODEL_IP175D) + { + // Reset the Switch + err = phydev->bus->write(phydev->bus, + IP175D_SOFT_RESET_REG_PHY, + IP175D_SOFT_RESET_REG_NUM, + IP175D_RESET_VAL); + if (err < 0) + return err; + mdelay(2); - /* Set MII0 speed and duplex (in PHY mode) */ - err = phydev->bus->write(phydev->bus, 29, 22, 0x420); - if (err < 0) - return err; + // Reset Vlan Table. PHY22 MII0, MSB. + err = phydev->bus->write(phydev->bus, IP175D_VLAN_CLS_REG_PHY, + IP175D_VLAN_CLS_REG_NUM, + VLAN_TABLE_CLR); + if (err < 0) + return err; + + // SET TPID Vlan. Ox8100. + err = phydev->bus->write(phydev->bus, + IP175D_VLAN_TPID_VAL_REG_PHY, + IP175D_VLAN_TPID_VAL_REG_NUM, + 0x8100); + if (err < 0) + return err; + } /* reset switch ports */ for (i = 0; i < 5; i++) { - err = phydev->bus->write(phydev->bus, i, - MII_BMCR, BMCR_RESET); + err = phydev->bus->write(phydev->bus, i, MII_BMCR, BMCR_RESET); if (err < 0) return err; } @@ -92,14 +366,15 @@ static int ip175c_config_init(struct phy_device *phydev) return 0; } -static int ip175c_read_status(struct phy_device *phydev) + +static int ip175x_read_status(struct phy_device *phydev) { int i, status; if (phydev->addr == 4) /* WAN port */ genphy_read_status(phydev); - if (phydev->addr == 5) /* MAC 5 */ + if (phydev->addr == 5) /* MAC 5 */ { /* Read all links status */ phydev->link = 0; @@ -116,11 +391,10 @@ static int ip175c_read_status(struct phy_device *phydev) phydev->link = 1; } } - return 0; } -static int ip175c_config_aneg(struct phy_device *phydev) +static int ip175x_config_aneg(struct phy_device *phydev) { if (phydev->addr == 5) /* MAC 5 */ { @@ -135,26 +409,29 @@ static int ip175c_config_aneg(struct phy_device *phydev) return 0; } -static struct phy_driver ip175c_driver = { +static struct phy_driver ip175x_driver = { .phy_id = 0x02430d80, - .name = "ICPlus IP175C", + .name = "ICPlus ip175x", .phy_id_mask = 0x0ffffff0, .features = PHY_BASIC_FEATURES, - .config_init = &ip175c_config_init, - .config_aneg = &ip175c_config_aneg, - .read_status = &ip175c_read_status, + .config_init = &ip175x_config_init, + .probe = &ip175x_probe, + .config_aneg = &ip175x_config_aneg, + .read_status = &ip175x_read_status, .driver = { .owner = THIS_MODULE,}, + .read_vlan_table = &ip175x_read_vlan_table, + .write_vlan_table = &ip175x_write_vlan_table, }; -static int __init ip175c_init(void) +static int __init ip175x_init(void) { - return phy_driver_register(&ip175c_driver); + return phy_driver_register(&ip175x_driver); } -static void __exit ip175c_exit(void) +static void __exit ip175x_exit(void) { - phy_driver_unregister(&ip175c_driver); + phy_driver_unregister(&ip175x_driver); } -module_init(ip175c_init); -module_exit(ip175c_exit); +module_init(ip175x_init); +module_exit(ip175x_exit); diff --git a/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.h b/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.h new file mode 100644 index 0000000000..b902e81251 --- /dev/null +++ b/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.h @@ -0,0 +1,91 @@ +#ifndef _IP175_H +#define _IP175_H + +#define DRV_NAME "ICPlus_IP175" +#define DRV_VERSION "1.1" +#define DRV_RELDATE "Sep 20th, 2010" + +/*****************************************************/ +/***************** ICPLUS_IP175D *******************/ +/*****************************************************/ + +#define IP175D_VLAN_TABLE_SIZE 16 +#define IP175D_VLAN_TABLE_COMMON_PHY 5 +#define IP175D_VLAN_TABLE_NO_TAG_LINE 6 + +#define IP175D_RESET_VAL 0x175D +#define IP175C_RESET_VAL 0x175C + + +#define SETBIT(x,y) ((x) |= (1<<(y))) +#define CLEARBIT(x,y) ((x) &= (~(1<<(y)))) + +/************************************ + VLAN Group Control Register +************************************/ + +#define IP175D_SOFT_RESET_REG_PHY 20 +#define IP175D_SOFT_RESET_REG_NUM 2 + +#define IP175D_VLAN_CLS_REG_PHY 22 +#define IP175D_VLAN_CLS_REG_NUM 0 + +#define VLAN_TABLE_CLR 0x8000 +#define UNVID_MODE_DISCARD 0x0000 +#define UNVID_MODE_FORWARD_TO_CPU 0x1000 +#define UNVID_MODE_FLOOD_PACKET 0x2000 +#define UNVID_MODE_RESERVED 0x3000 +#define VLAN_CLS_USE_VID(p) (0) +#define VLAN_CLS_USE_PVID(p) (1<(6+p)) +#define VLAN_MODE_PORT_BASED(p) (0) +#define VLAN_MODE_TAGGED_BASED(p) (1