1 Commit-Id: 1e8820256f9921370cd7423396871e2d850e0323
2 From: Divy Le Ray <divy@chelsio.com>
3 Date: Wed, 8 Oct 2008 17:40:07 -0700
4 Acked-by: Karsten Keil <kkeil@novell.com>
5 Subject: [PATCH] cxgb3: Support for Aeluros 2005 PHY
9 Add support for SR PHY.
10 Auto-detect phy module type, and report type changes.
12 Signed-off-by: Divy Le Ray <divy@chelsio.com>
13 Signed-off-by: David S. Miller <davem@davemloft.net>
15 Index: linux-2.6.27/drivers/net/cxgb3/ael1002.c
16 ===================================================================
17 --- linux-2.6.27.orig/drivers/net/cxgb3/ael1002.c
18 +++ linux-2.6.27/drivers/net/cxgb3/ael1002.c
23 + PMD_RSD = 10, /* PMA/PMD receive signal detect register */
24 + PCS_STAT1_X = 24, /* 10GBASE-X PCS status 1 register */
25 + PCS_STAT1_R = 32, /* 10GBASE-R PCS status 1 register */
26 + XS_LN_STAT = 24 /* XS lane status register */
30 AEL100X_TX_DISABLE = 9,
31 AEL100X_TX_CONFIG1 = 0xc002,
32 AEL1002_PWR_DOWN_HI = 0xc011,
33 AEL1002_PWR_DOWN_LO = 0xc012,
34 AEL1002_XFI_EQL = 0xc015,
35 AEL1002_LB_EN = 0xc017,
36 + AEL_OPT_SETTINGS = 0xc017,
37 + AEL_I2C_CTRL = 0xc30a,
38 + AEL_I2C_DATA = 0xc30b,
39 + AEL_I2C_STAT = 0xc30c,
40 + AEL2005_GPIO_CTRL = 0xc214,
41 + AEL2005_GPIO_STAT = 0xc215,
44 +enum { edc_none, edc_sr, edc_twinax };
46 +/* PHY module I2C device address */
47 +#define MODULE_DEV_ADDR 0xa0
49 +#define AEL2005_MODDET_IRQ 4
52 + unsigned short mmd_addr;
53 + unsigned short reg_addr;
54 + unsigned short clear_bits;
55 + unsigned short set_bits;
58 +static int set_phy_regs(struct cphy *phy, const struct reg_val *rv)
62 + for (err = 0; rv->mmd_addr && !err; rv++) {
63 + if (rv->clear_bits == 0xffff)
64 + err = mdio_write(phy, rv->mmd_addr, rv->reg_addr,
67 + err = t3_mdio_change_bits(phy, rv->mmd_addr,
68 + rv->reg_addr, rv->clear_bits,
74 static void ael100x_txon(struct cphy *phy)
76 int tx_on_gpio = phy->addr == 0 ? F_GPIO7_OUT_VAL : F_GPIO2_OUT_VAL;
77 @@ -81,23 +124,23 @@ static int ael1002_intr_noop(struct cphy
81 -static int ael100x_get_link_status(struct cphy *phy, int *link_ok,
82 - int *speed, int *duplex, int *fc)
84 + * Get link status for a 10GBASE-R device.
86 +static int get_link_status_r(struct cphy *phy, int *link_ok, int *speed,
87 + int *duplex, int *fc)
90 - unsigned int status;
91 - int err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR, &status);
92 + unsigned int stat0, stat1, stat2;
93 + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, PMD_RSD, &stat0);
96 - * BMSR_LSTATUS is latch-low, so if it is 0 we need to read it
97 - * once more to get the current link state.
99 - if (!err && !(status & BMSR_LSTATUS))
100 - err = mdio_read(phy, MDIO_DEV_PMA_PMD, MII_BMSR,
103 + err = mdio_read(phy, MDIO_DEV_PCS, PCS_STAT1_R, &stat1);
105 + err = mdio_read(phy, MDIO_DEV_XGXS, XS_LN_STAT, &stat2);
108 - *link_ok = !!(status & BMSR_LSTATUS);
109 + *link_ok = (stat0 & stat1 & (stat2 >> 12)) & 1;
112 *speed = SPEED_10000;
113 @@ -112,7 +155,7 @@ static struct cphy_ops ael1002_ops = {
114 .intr_disable = ael1002_intr_noop,
115 .intr_clear = ael1002_intr_noop,
116 .intr_handler = ael1002_intr_noop,
117 - .get_link_status = ael100x_get_link_status,
118 + .get_link_status = get_link_status_r,
119 .power_down = ael1002_power_down,
122 @@ -143,7 +186,7 @@ static struct cphy_ops ael1006_ops = {
123 .intr_disable = t3_phy_lasi_intr_disable,
124 .intr_clear = t3_phy_lasi_intr_clear,
125 .intr_handler = t3_phy_lasi_intr_handler,
126 - .get_link_status = ael100x_get_link_status,
127 + .get_link_status = get_link_status_r,
128 .power_down = ael1006_power_down,
131 @@ -157,13 +200,948 @@ int t3_ael1006_phy_prep(struct cphy *phy
135 +static int ael2005_setup_sr_edc(struct cphy *phy)
137 + static struct reg_val regs[] = {
138 + { MDIO_DEV_PMA_PMD, 0xc003, 0xffff, 0x181 },
139 + { MDIO_DEV_PMA_PMD, 0xc010, 0xffff, 0x448a },
140 + { MDIO_DEV_PMA_PMD, 0xc04a, 0xffff, 0x5200 },
143 + static u16 sr_edc[] = {
418 + err = set_phy_regs(phy, regs);
424 + for (i = 0; i < ARRAY_SIZE(sr_edc) && !err; i += 2)
425 + err = mdio_write(phy, MDIO_DEV_PMA_PMD, sr_edc[i],
428 + phy->priv = edc_sr;
432 +static int ael2005_setup_twinax_edc(struct cphy *phy, int modtype)
434 + static struct reg_val regs[] = {
435 + { MDIO_DEV_PMA_PMD, 0xc04a, 0xffff, 0x5a00 },
438 + static struct reg_val preemphasis[] = {
439 + { MDIO_DEV_PMA_PMD, 0xc014, 0xffff, 0xfe16 },
440 + { MDIO_DEV_PMA_PMD, 0xc015, 0xffff, 0xa000 },
443 + static u16 twinax_edc[] = {
813 + err = set_phy_regs(phy, regs);
814 + if (!err && modtype == phy_modtype_twinax_long)
815 + err = set_phy_regs(phy, preemphasis);
821 + for (i = 0; i < ARRAY_SIZE(twinax_edc) && !err; i += 2)
822 + err = mdio_write(phy, MDIO_DEV_PMA_PMD, twinax_edc[i],
823 + twinax_edc[i + 1]);
825 + phy->priv = edc_twinax;
829 +static int ael2005_i2c_rd(struct cphy *phy, int dev_addr, int word_addr)
832 + unsigned int stat, data;
834 + err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL_I2C_CTRL,
835 + (dev_addr << 8) | (1 << 8) | word_addr);
839 + for (i = 0; i < 5; i++) {
841 + err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_STAT, &stat);
844 + if ((stat & 3) == 1) {
845 + err = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL_I2C_DATA,
852 + CH_WARN(phy->adapter, "PHY %u I2C read of addr %u timed out\n",
853 + phy->addr, word_addr);
857 +static int get_module_type(struct cphy *phy, int delay_ms)
862 + v = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, &stat);
866 + if (stat & (1 << 8)) /* module absent */
867 + return phy_modtype_none;
872 + /* see SFF-8472 for below */
873 + v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 3);
878 + return phy_modtype_sr;
880 + return phy_modtype_lr;
882 + return phy_modtype_lrm;
884 + v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 6);
890 + v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 10);
895 + v = ael2005_i2c_rd(phy, MODULE_DEV_ADDR, 0x12);
898 + return v > 10 ? phy_modtype_twinax_long : phy_modtype_twinax;
901 + return phy_modtype_unknown;
904 +static int ael2005_intr_enable(struct cphy *phy)
906 + int err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, 0x200);
907 + return err ? err : t3_phy_lasi_intr_enable(phy);
910 +static int ael2005_intr_disable(struct cphy *phy)
912 + int err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, 0x100);
913 + return err ? err : t3_phy_lasi_intr_disable(phy);
916 +static int ael2005_intr_clear(struct cphy *phy)
918 + int err = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL, 0xd00);
919 + return err ? err : t3_phy_lasi_intr_clear(phy);
922 +static int ael2005_reset(struct cphy *phy, int wait)
924 + static struct reg_val regs0[] = {
925 + { MDIO_DEV_PMA_PMD, 0xc001, 0, 1 << 5 },
926 + { MDIO_DEV_PMA_PMD, 0xc017, 0, 1 << 5 },
927 + { MDIO_DEV_PMA_PMD, 0xc013, 0xffff, 0xf341 },
928 + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0x8000 },
929 + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0x8100 },
930 + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0x8000 },
931 + { MDIO_DEV_PMA_PMD, 0xc210, 0xffff, 0 },
934 + static struct reg_val regs1[] = {
935 + { MDIO_DEV_PMA_PMD, 0xca00, 0xffff, 0x0080 },
936 + { MDIO_DEV_PMA_PMD, 0xca12, 0xffff, 0 },
940 + int err, lasi_ctrl;
942 + err = mdio_read(phy, MDIO_DEV_PMA_PMD, LASI_CTRL, &lasi_ctrl);
946 + err = t3_phy_reset(phy, MDIO_DEV_PMA_PMD, 0);
951 + phy->priv = edc_none;
952 + err = set_phy_regs(phy, regs0);
958 + err = get_module_type(phy, 0);
961 + phy->modtype = err;
963 + if (err == phy_modtype_twinax || err == phy_modtype_twinax_long)
964 + err = ael2005_setup_twinax_edc(phy, err);
966 + err = ael2005_setup_sr_edc(phy);
970 + err = set_phy_regs(phy, regs1);
974 + /* reset wipes out interrupts, reenable them if they were on */
976 + err = ael2005_intr_enable(phy);
980 +static int ael2005_intr_handler(struct cphy *phy)
983 + int ret, edc_needed, cause = 0;
985 + ret = mdio_read(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_STAT, &stat);
989 + if (stat & AEL2005_MODDET_IRQ) {
990 + ret = mdio_write(phy, MDIO_DEV_PMA_PMD, AEL2005_GPIO_CTRL,
995 + /* modules have max 300 ms init time after hot plug */
996 + ret = get_module_type(phy, 300);
1000 + phy->modtype = ret;
1001 + if (ret == phy_modtype_none)
1002 + edc_needed = phy->priv; /* on unplug retain EDC */
1003 + else if (ret == phy_modtype_twinax ||
1004 + ret == phy_modtype_twinax_long)
1005 + edc_needed = edc_twinax;
1007 + edc_needed = edc_sr;
1009 + if (edc_needed != phy->priv) {
1010 + ret = ael2005_reset(phy, 0);
1011 + return ret ? ret : cphy_cause_module_change;
1013 + cause = cphy_cause_module_change;
1016 + ret = t3_phy_lasi_intr_handler(phy);
1021 + return ret ? ret : cphy_cause_link_change;
1024 +static struct cphy_ops ael2005_ops = {
1025 + .reset = ael2005_reset,
1026 + .intr_enable = ael2005_intr_enable,
1027 + .intr_disable = ael2005_intr_disable,
1028 + .intr_clear = ael2005_intr_clear,
1029 + .intr_handler = ael2005_intr_handler,
1030 + .get_link_status = get_link_status_r,
1031 + .power_down = ael1002_power_down,
1034 +int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter,
1035 + int phy_addr, const struct mdio_ops *mdio_ops)
1037 + cphy_init(phy, adapter, phy_addr, &ael2005_ops, mdio_ops,
1038 + SUPPORTED_10000baseT_Full | SUPPORTED_AUI | SUPPORTED_FIBRE |
1039 + SUPPORTED_IRQ, "10GBASE-R");
1041 + return t3_mdio_change_bits(phy, MDIO_DEV_PMA_PMD, AEL_OPT_SETTINGS, 0,
1046 + * Get link status for a 10GBASE-X device.
1048 +static int get_link_status_x(struct cphy *phy, int *link_ok, int *speed,
1049 + int *duplex, int *fc)
1052 + unsigned int stat0, stat1, stat2;
1053 + int err = mdio_read(phy, MDIO_DEV_PMA_PMD, PMD_RSD, &stat0);
1056 + err = mdio_read(phy, MDIO_DEV_PCS, PCS_STAT1_X, &stat1);
1058 + err = mdio_read(phy, MDIO_DEV_XGXS, XS_LN_STAT, &stat2);
1061 + *link_ok = (stat0 & (stat1 >> 12) & (stat2 >> 12)) & 1;
1064 + *speed = SPEED_10000;
1066 + *duplex = DUPLEX_FULL;
1070 static struct cphy_ops qt2045_ops = {
1071 .reset = ael1006_reset,
1072 .intr_enable = t3_phy_lasi_intr_enable,
1073 .intr_disable = t3_phy_lasi_intr_disable,
1074 .intr_clear = t3_phy_lasi_intr_clear,
1075 .intr_handler = t3_phy_lasi_intr_handler,
1076 - .get_link_status = ael100x_get_link_status,
1077 + .get_link_status = get_link_status_x,
1078 .power_down = ael1006_power_down,
1081 Index: linux-2.6.27/drivers/net/cxgb3/common.h
1082 ===================================================================
1083 --- linux-2.6.27.orig/drivers/net/cxgb3/common.h
1084 +++ linux-2.6.27/drivers/net/cxgb3/common.h
1085 @@ -547,7 +547,19 @@ enum {
1086 /* PHY interrupt types */
1088 cphy_cause_link_change = 1,
1089 - cphy_cause_fifo_error = 2
1090 + cphy_cause_fifo_error = 2,
1091 + cphy_cause_module_change = 4,
1094 +/* PHY module types */
1100 + phy_modtype_twinax,
1101 + phy_modtype_twinax_long,
1102 + phy_modtype_unknown
1105 /* PHY operations */
1106 @@ -572,7 +584,9 @@ struct cphy_ops {
1108 /* A PHY instance */
1110 - int addr; /* PHY address */
1111 + u8 addr; /* PHY address */
1112 + u8 modtype; /* PHY module type */
1113 + short priv; /* scratch pad */
1114 unsigned int caps; /* PHY capabilities */
1115 struct adapter *adapter; /* associated adapter */
1116 const char *desc; /* PHY description */
1117 @@ -793,6 +807,8 @@ int t3_ael1002_phy_prep(struct cphy *phy
1118 int phy_addr, const struct mdio_ops *mdio_ops);
1119 int t3_ael1006_phy_prep(struct cphy *phy, struct adapter *adapter,
1120 int phy_addr, const struct mdio_ops *mdio_ops);
1121 +int t3_ael2005_phy_prep(struct cphy *phy, struct adapter *adapter,
1122 + int phy_addr, const struct mdio_ops *mdio_ops);
1123 int t3_qt2045_phy_prep(struct cphy *phy, struct adapter *adapter, int phy_addr,
1124 const struct mdio_ops *mdio_ops);
1125 int t3_xaui_direct_phy_prep(struct cphy *phy, struct adapter *adapter,
1126 Index: linux-2.6.27/drivers/net/cxgb3/cxgb3_main.c
1127 ===================================================================
1128 --- linux-2.6.27.orig/drivers/net/cxgb3/cxgb3_main.c
1129 +++ linux-2.6.27/drivers/net/cxgb3/cxgb3_main.c
1130 @@ -209,6 +209,31 @@ void t3_os_link_changed(struct adapter *
1135 + * t3_os_phymod_changed - handle PHY module changes
1136 + * @phy: the PHY reporting the module change
1137 + * @mod_type: new module type
1139 + * This is the OS-dependent handler for PHY module changes. It is
1140 + * invoked when a PHY module is removed or inserted for any OS-specific
1143 +void t3_os_phymod_changed(struct adapter *adap, int port_id)
1145 + static const char *mod_str[] = {
1146 + NULL, "SR", "LR", "LRM", "TWINAX", "TWINAX", "unknown"
1149 + const struct net_device *dev = adap->port[port_id];
1150 + const struct port_info *pi = netdev_priv(dev);
1152 + if (pi->phy.modtype == phy_modtype_none)
1153 + printk(KERN_INFO "%s: PHY module unplugged\n", dev->name);
1155 + printk(KERN_INFO "%s: %s PHY module inserted\n", dev->name,
1156 + mod_str[pi->phy.modtype]);
1159 static void cxgb_set_rxmode(struct net_device *dev)
1161 struct t3_rx_mode rm;
1162 Index: linux-2.6.27/drivers/net/cxgb3/t3_hw.c
1163 ===================================================================
1164 --- linux-2.6.27.orig/drivers/net/cxgb3/t3_hw.c
1165 +++ linux-2.6.27/drivers/net/cxgb3/t3_hw.c
1166 @@ -511,7 +511,7 @@ static const struct port_type_info port_
1167 { t3_vsc8211_phy_prep },
1169 { t3_xaui_direct_phy_prep },
1171 + { t3_ael2005_phy_prep },
1172 { t3_qt2045_phy_prep },
1173 { t3_ael1006_phy_prep },
1175 @@ -1728,6 +1728,8 @@ int t3_phy_intr_handler(struct adapter *
1176 t3_link_changed(adapter, i);
1177 if (phy_cause & cphy_cause_fifo_error)
1178 p->phy.fifo_errors++;
1179 + if (phy_cause & cphy_cause_module_change)
1180 + t3_os_phymod_changed(adapter, i);