summaryrefslogtreecommitdiff
path: root/cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c
diff options
context:
space:
mode:
Diffstat (limited to 'cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c')
-rw-r--r--cleopatre/linux-2.6.25.10-spc300/drivers/net/phy/icplus.c367
1 files changed, 322 insertions, 45 deletions
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 <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
@@ -31,44 +40,309 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
-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);