aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/niu.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/niu.c')
-rw-r--r--drivers/net/niu.c371
1 files changed, 330 insertions, 41 deletions
diff --git a/drivers/net/niu.c b/drivers/net/niu.c
index 7565c2d7f30e..4009c4ce96b4 100644
--- a/drivers/net/niu.c
+++ b/drivers/net/niu.c
@@ -33,8 +33,8 @@
33 33
34#define DRV_MODULE_NAME "niu" 34#define DRV_MODULE_NAME "niu"
35#define PFX DRV_MODULE_NAME ": " 35#define PFX DRV_MODULE_NAME ": "
36#define DRV_MODULE_VERSION "0.7" 36#define DRV_MODULE_VERSION "0.8"
37#define DRV_MODULE_RELDATE "February 18, 2008" 37#define DRV_MODULE_RELDATE "April 24, 2008"
38 38
39static char version[] __devinitdata = 39static char version[] __devinitdata =
40 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n"; 40 DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";
@@ -673,11 +673,16 @@ static int serdes_init_10g(struct niu *np)
673 } 673 }
674 674
675 if ((sig & mask) != val) { 675 if ((sig & mask) != val) {
676 if (np->flags & NIU_FLAGS_HOTPLUG_PHY) {
677 np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
678 return 0;
679 }
676 dev_err(np->device, PFX "Port %u signal bits [%08x] are not " 680 dev_err(np->device, PFX "Port %u signal bits [%08x] are not "
677 "[%08x]\n", np->port, (int) (sig & mask), (int) val); 681 "[%08x]\n", np->port, (int) (sig & mask), (int) val);
678 return -ENODEV; 682 return -ENODEV;
679 } 683 }
680 684 if (np->flags & NIU_FLAGS_HOTPLUG_PHY)
685 np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT;
681 return 0; 686 return 0;
682} 687}
683 688
@@ -998,6 +1003,28 @@ static int bcm8704_user_dev3_readback(struct niu *np, int reg)
998 return 0; 1003 return 0;
999} 1004}
1000 1005
1006static int bcm8706_init_user_dev3(struct niu *np)
1007{
1008 int err;
1009
1010
1011 err = mdio_read(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
1012 BCM8704_USER_OPT_DIGITAL_CTRL);
1013 if (err < 0)
1014 return err;
1015 err &= ~USER_ODIG_CTRL_GPIOS;
1016 err |= (0x3 << USER_ODIG_CTRL_GPIOS_SHIFT);
1017 err |= USER_ODIG_CTRL_RESV2;
1018 err = mdio_write(np, np->phy_addr, BCM8704_USER_DEV3_ADDR,
1019 BCM8704_USER_OPT_DIGITAL_CTRL, err);
1020 if (err)
1021 return err;
1022
1023 mdelay(1000);
1024
1025 return 0;
1026}
1027
1001static int bcm8704_init_user_dev3(struct niu *np) 1028static int bcm8704_init_user_dev3(struct niu *np)
1002{ 1029{
1003 int err; 1030 int err;
@@ -1127,33 +1154,11 @@ static int xcvr_init_10g_mrvl88x2011(struct niu *np)
1127 MRVL88X2011_10G_PMD_TX_DIS, MRVL88X2011_ENA_PMDTX); 1154 MRVL88X2011_10G_PMD_TX_DIS, MRVL88X2011_ENA_PMDTX);
1128} 1155}
1129 1156
1130static int xcvr_init_10g_bcm8704(struct niu *np) 1157
1158static int xcvr_diag_bcm870x(struct niu *np)
1131{ 1159{
1132 struct niu_link_config *lp = &np->link_config;
1133 u16 analog_stat0, tx_alarm_status; 1160 u16 analog_stat0, tx_alarm_status;
1134 int err; 1161 int err = 0;
1135
1136 err = bcm8704_reset(np);
1137 if (err)
1138 return err;
1139
1140 err = bcm8704_init_user_dev3(np);
1141 if (err)
1142 return err;
1143
1144 err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
1145 MII_BMCR);
1146 if (err < 0)
1147 return err;
1148 err &= ~BMCR_LOOPBACK;
1149
1150 if (lp->loopback_mode == LOOPBACK_MAC)
1151 err |= BMCR_LOOPBACK;
1152
1153 err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
1154 MII_BMCR, err);
1155 if (err)
1156 return err;
1157 1162
1158#if 1 1163#if 1
1159 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR, 1164 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
@@ -1211,6 +1216,89 @@ static int xcvr_init_10g_bcm8704(struct niu *np)
1211 return 0; 1216 return 0;
1212} 1217}
1213 1218
1219static int xcvr_10g_set_lb_bcm870x(struct niu *np)
1220{
1221 struct niu_link_config *lp = &np->link_config;
1222 int err;
1223
1224 err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
1225 MII_BMCR);
1226 if (err < 0)
1227 return err;
1228
1229 err &= ~BMCR_LOOPBACK;
1230
1231 if (lp->loopback_mode == LOOPBACK_MAC)
1232 err |= BMCR_LOOPBACK;
1233
1234 err = mdio_write(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
1235 MII_BMCR, err);
1236 if (err)
1237 return err;
1238
1239 return 0;
1240}
1241
1242static int xcvr_init_10g_bcm8706(struct niu *np)
1243{
1244 int err = 0;
1245 u64 val;
1246
1247 if ((np->flags & NIU_FLAGS_HOTPLUG_PHY) &&
1248 (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) == 0)
1249 return err;
1250
1251 val = nr64_mac(XMAC_CONFIG);
1252 val &= ~XMAC_CONFIG_LED_POLARITY;
1253 val |= XMAC_CONFIG_FORCE_LED_ON;
1254 nw64_mac(XMAC_CONFIG, val);
1255
1256 val = nr64(MIF_CONFIG);
1257 val |= MIF_CONFIG_INDIRECT_MODE;
1258 nw64(MIF_CONFIG, val);
1259
1260 err = bcm8704_reset(np);
1261 if (err)
1262 return err;
1263
1264 err = xcvr_10g_set_lb_bcm870x(np);
1265 if (err)
1266 return err;
1267
1268 err = bcm8706_init_user_dev3(np);
1269 if (err)
1270 return err;
1271
1272 err = xcvr_diag_bcm870x(np);
1273 if (err)
1274 return err;
1275
1276 return 0;
1277}
1278
1279static int xcvr_init_10g_bcm8704(struct niu *np)
1280{
1281 int err;
1282
1283 err = bcm8704_reset(np);
1284 if (err)
1285 return err;
1286
1287 err = bcm8704_init_user_dev3(np);
1288 if (err)
1289 return err;
1290
1291 err = xcvr_10g_set_lb_bcm870x(np);
1292 if (err)
1293 return err;
1294
1295 err = xcvr_diag_bcm870x(np);
1296 if (err)
1297 return err;
1298
1299 return 0;
1300}
1301
1214static int xcvr_init_10g(struct niu *np) 1302static int xcvr_init_10g(struct niu *np)
1215{ 1303{
1216 int phy_id, err; 1304 int phy_id, err;
@@ -1548,6 +1636,59 @@ out:
1548 return err; 1636 return err;
1549} 1637}
1550 1638
1639static int link_status_10g_bcm8706(struct niu *np, int *link_up_p)
1640{
1641 int err, link_up;
1642 link_up = 0;
1643
1644 err = mdio_read(np, np->phy_addr, BCM8704_PMA_PMD_DEV_ADDR,
1645 BCM8704_PMD_RCV_SIGDET);
1646 if (err < 0)
1647 goto out;
1648 if (!(err & PMD_RCV_SIGDET_GLOBAL)) {
1649 err = 0;
1650 goto out;
1651 }
1652
1653 err = mdio_read(np, np->phy_addr, BCM8704_PCS_DEV_ADDR,
1654 BCM8704_PCS_10G_R_STATUS);
1655 if (err < 0)
1656 goto out;
1657
1658 if (!(err & PCS_10G_R_STATUS_BLK_LOCK)) {
1659 err = 0;
1660 goto out;
1661 }
1662
1663 err = mdio_read(np, np->phy_addr, BCM8704_PHYXS_DEV_ADDR,
1664 BCM8704_PHYXS_XGXS_LANE_STAT);
1665 if (err < 0)
1666 goto out;
1667 if (err != (PHYXS_XGXS_LANE_STAT_ALINGED |
1668 PHYXS_XGXS_LANE_STAT_MAGIC |
1669 PHYXS_XGXS_LANE_STAT_PATTEST |
1670 PHYXS_XGXS_LANE_STAT_LANE3 |
1671 PHYXS_XGXS_LANE_STAT_LANE2 |
1672 PHYXS_XGXS_LANE_STAT_LANE1 |
1673 PHYXS_XGXS_LANE_STAT_LANE0)) {
1674 err = 0;
1675 np->link_config.active_speed = SPEED_INVALID;
1676 np->link_config.active_duplex = DUPLEX_INVALID;
1677 goto out;
1678 }
1679
1680 link_up = 1;
1681 np->link_config.active_speed = SPEED_10000;
1682 np->link_config.active_duplex = DUPLEX_FULL;
1683 err = 0;
1684
1685out:
1686 *link_up_p = link_up;
1687 if (np->flags & NIU_FLAGS_HOTPLUG_PHY)
1688 err = 0;
1689 return err;
1690}
1691
1551static int link_status_10g_bcom(struct niu *np, int *link_up_p) 1692static int link_status_10g_bcom(struct niu *np, int *link_up_p)
1552{ 1693{
1553 int err, link_up; 1694 int err, link_up;
@@ -1627,6 +1768,82 @@ static int link_status_10g(struct niu *np, int *link_up_p)
1627 return err; 1768 return err;
1628} 1769}
1629 1770
1771static int niu_10g_phy_present(struct niu *np)
1772{
1773 u64 sig, mask, val;
1774
1775 sig = nr64(ESR_INT_SIGNALS);
1776 switch (np->port) {
1777 case 0:
1778 mask = ESR_INT_SIGNALS_P0_BITS;
1779 val = (ESR_INT_SRDY0_P0 |
1780 ESR_INT_DET0_P0 |
1781 ESR_INT_XSRDY_P0 |
1782 ESR_INT_XDP_P0_CH3 |
1783 ESR_INT_XDP_P0_CH2 |
1784 ESR_INT_XDP_P0_CH1 |
1785 ESR_INT_XDP_P0_CH0);
1786 break;
1787
1788 case 1:
1789 mask = ESR_INT_SIGNALS_P1_BITS;
1790 val = (ESR_INT_SRDY0_P1 |
1791 ESR_INT_DET0_P1 |
1792 ESR_INT_XSRDY_P1 |
1793 ESR_INT_XDP_P1_CH3 |
1794 ESR_INT_XDP_P1_CH2 |
1795 ESR_INT_XDP_P1_CH1 |
1796 ESR_INT_XDP_P1_CH0);
1797 break;
1798
1799 default:
1800 return 0;
1801 }
1802
1803 if ((sig & mask) != val)
1804 return 0;
1805 return 1;
1806}
1807
1808static int link_status_10g_hotplug(struct niu *np, int *link_up_p)
1809{
1810 unsigned long flags;
1811 int err = 0;
1812 int phy_present;
1813 int phy_present_prev;
1814
1815 spin_lock_irqsave(&np->lock, flags);
1816
1817 if (np->link_config.loopback_mode == LOOPBACK_DISABLED) {
1818 phy_present_prev = (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT) ?
1819 1 : 0;
1820 phy_present = niu_10g_phy_present(np);
1821 if (phy_present != phy_present_prev) {
1822 /* state change */
1823 if (phy_present) {
1824 np->flags |= NIU_FLAGS_HOTPLUG_PHY_PRESENT;
1825 if (np->phy_ops->xcvr_init)
1826 err = np->phy_ops->xcvr_init(np);
1827 if (err) {
1828 /* debounce */
1829 np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
1830 }
1831 } else {
1832 np->flags &= ~NIU_FLAGS_HOTPLUG_PHY_PRESENT;
1833 *link_up_p = 0;
1834 niuwarn(LINK, "%s: Hotplug PHY Removed\n",
1835 np->dev->name);
1836 }
1837 }
1838 if (np->flags & NIU_FLAGS_HOTPLUG_PHY_PRESENT)
1839 err = link_status_10g_bcm8706(np, link_up_p);
1840 }
1841
1842 spin_unlock_irqrestore(&np->lock, flags);
1843
1844 return err;
1845}
1846
1630static int link_status_1g(struct niu *np, int *link_up_p) 1847static int link_status_1g(struct niu *np, int *link_up_p)
1631{ 1848{
1632 struct niu_link_config *lp = &np->link_config; 1849 struct niu_link_config *lp = &np->link_config;
@@ -1761,6 +1978,12 @@ static const struct niu_phy_ops phy_ops_10g_fiber = {
1761 .link_status = link_status_10g, 1978 .link_status = link_status_10g,
1762}; 1979};
1763 1980
1981static const struct niu_phy_ops phy_ops_10g_fiber_hotplug = {
1982 .serdes_init = serdes_init_10g,
1983 .xcvr_init = xcvr_init_10g_bcm8706,
1984 .link_status = link_status_10g_hotplug,
1985};
1986
1764static const struct niu_phy_ops phy_ops_10g_copper = { 1987static const struct niu_phy_ops phy_ops_10g_copper = {
1765 .serdes_init = serdes_init_10g, 1988 .serdes_init = serdes_init_10g,
1766 .link_status = link_status_10g, /* XXX */ 1989 .link_status = link_status_10g, /* XXX */
@@ -1792,6 +2015,11 @@ static const struct niu_phy_template phy_template_10g_fiber = {
1792 .phy_addr_base = 8, 2015 .phy_addr_base = 8,
1793}; 2016};
1794 2017
2018static const struct niu_phy_template phy_template_10g_fiber_hotplug = {
2019 .ops = &phy_ops_10g_fiber_hotplug,
2020 .phy_addr_base = 8,
2021};
2022
1795static const struct niu_phy_template phy_template_10g_copper = { 2023static const struct niu_phy_template phy_template_10g_copper = {
1796 .ops = &phy_ops_10g_copper, 2024 .ops = &phy_ops_10g_copper,
1797 .phy_addr_base = 10, 2025 .phy_addr_base = 10,
@@ -1996,6 +2224,13 @@ static int niu_determine_phy_disposition(struct niu *np)
1996 plat_type == PLAT_TYPE_VF_P1) 2224 plat_type == PLAT_TYPE_VF_P1)
1997 phy_addr_off = 8; 2225 phy_addr_off = 8;
1998 phy_addr_off += np->port; 2226 phy_addr_off += np->port;
2227 if (np->flags & NIU_FLAGS_HOTPLUG_PHY) {
2228 tp = &phy_template_10g_fiber_hotplug;
2229 if (np->port == 0)
2230 phy_addr_off = 8;
2231 if (np->port == 1)
2232 phy_addr_off = 12;
2233 }
1999 break; 2234 break;
2000 2235
2001 case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES: 2236 case NIU_FLAGS_10G | NIU_FLAGS_XCVR_SERDES:
@@ -6773,6 +7008,37 @@ static int __devinit niu_phy_type_prop_decode(struct niu *np,
6773 return 0; 7008 return 0;
6774} 7009}
6775 7010
7011/* niu board models have a trailing dash version incremented
7012 * with HW rev change. Need to ingnore the dash version while
7013 * checking for match
7014 *
7015 * for example, for the 10G card the current vpd.board_model
7016 * is 501-5283-04, of which -04 is the dash version and have
7017 * to be ignored
7018 */
7019static int niu_board_model_match(struct niu *np, const char *model)
7020{
7021 return !strncmp(np->vpd.board_model, model, strlen(model));
7022}
7023
7024static int niu_pci_vpd_get_nports(struct niu *np)
7025{
7026 int ports = 0;
7027
7028 if ((niu_board_model_match(np, NIU_QGC_LP_BM_STR)) ||
7029 (niu_board_model_match(np, NIU_QGC_PEM_BM_STR)) ||
7030 (niu_board_model_match(np, NIU_ALONSO_BM_STR))) {
7031 ports = 4;
7032 } else if ((niu_board_model_match(np, NIU_2XGF_LP_BM_STR)) ||
7033 (niu_board_model_match(np, NIU_2XGF_PEM_BM_STR)) ||
7034 (niu_board_model_match(np, NIU_FOXXY_BM_STR)) ||
7035 (niu_board_model_match(np, NIU_2XGF_MRVL_BM_STR))) {
7036 ports = 2;
7037 }
7038
7039 return ports;
7040}
7041
6776static void __devinit niu_pci_vpd_validate(struct niu *np) 7042static void __devinit niu_pci_vpd_validate(struct niu *np)
6777{ 7043{
6778 struct net_device *dev = np->dev; 7044 struct net_device *dev = np->dev;
@@ -6799,6 +7065,9 @@ static void __devinit niu_pci_vpd_validate(struct niu *np)
6799 } 7065 }
6800 if (np->flags & NIU_FLAGS_10G) 7066 if (np->flags & NIU_FLAGS_10G)
6801 np->mac_xcvr = MAC_XCVR_XPCS; 7067 np->mac_xcvr = MAC_XCVR_XPCS;
7068 } else if (niu_board_model_match(np, NIU_FOXXY_BM_STR)) {
7069 np->flags |= (NIU_FLAGS_10G | NIU_FLAGS_FIBER |
7070 NIU_FLAGS_HOTPLUG_PHY);
6802 } else if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) { 7071 } else if (niu_phy_type_prop_decode(np, np->vpd.phy_type)) {
6803 dev_err(np->device, PFX "Illegal phy string [%s].\n", 7072 dev_err(np->device, PFX "Illegal phy string [%s].\n",
6804 np->vpd.phy_type); 7073 np->vpd.phy_type);
@@ -6987,11 +7256,17 @@ static int __devinit niu_get_and_validate_port(struct niu *np)
6987 if (parent->plat_type == PLAT_TYPE_NIU) { 7256 if (parent->plat_type == PLAT_TYPE_NIU) {
6988 parent->num_ports = 2; 7257 parent->num_ports = 2;
6989 } else { 7258 } else {
6990 parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) & 7259 parent->num_ports = niu_pci_vpd_get_nports(np);
6991 ESPC_NUM_PORTS_MACS_VAL; 7260 if (!parent->num_ports) {
6992 7261 /* Fall back to SPROM as last resort.
6993 if (!parent->num_ports) 7262 * This will fail on most cards.
6994 parent->num_ports = 4; 7263 */
7264 parent->num_ports = nr64(ESPC_NUM_PORTS_MACS) &
7265 ESPC_NUM_PORTS_MACS_VAL;
7266
7267 if (!parent->num_ports)
7268 return -ENODEV;
7269 }
6995 } 7270 }
6996 } 7271 }
6997 7272
@@ -7015,7 +7290,8 @@ static int __devinit phy_record(struct niu_parent *parent,
7015 return 0; 7290 return 0;
7016 if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) { 7291 if (type == PHY_TYPE_PMA_PMD || type == PHY_TYPE_PCS) {
7017 if (((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704) && 7292 if (((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8704) &&
7018 ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011)) 7293 ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_MRVL88X2011) &&
7294 ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM8706))
7019 return 0; 7295 return 0;
7020 } else { 7296 } else {
7021 if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R) 7297 if ((id & NIU_PHY_ID_MASK) != NIU_PHY_ID_BCM5464R)
@@ -7262,7 +7538,6 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent)
7262 u32 val; 7538 u32 val;
7263 int err; 7539 int err;
7264 7540
7265
7266 if (!strcmp(np->vpd.model, "SUNW,CP3220") || 7541 if (!strcmp(np->vpd.model, "SUNW,CP3220") ||
7267 !strcmp(np->vpd.model, "SUNW,CP3260")) { 7542 !strcmp(np->vpd.model, "SUNW,CP3260")) {
7268 num_10g = 0; 7543 num_10g = 0;
@@ -7273,6 +7548,12 @@ static int __devinit walk_phys(struct niu *np, struct niu_parent *parent)
7273 phy_encode(PORT_TYPE_1G, 1) | 7548 phy_encode(PORT_TYPE_1G, 1) |
7274 phy_encode(PORT_TYPE_1G, 2) | 7549 phy_encode(PORT_TYPE_1G, 2) |
7275 phy_encode(PORT_TYPE_1G, 3)); 7550 phy_encode(PORT_TYPE_1G, 3));
7551 } else if (niu_board_model_match(np, NIU_FOXXY_BM_STR)) {
7552 num_10g = 2;
7553 num_1g = 0;
7554 parent->num_ports = 2;
7555 val = (phy_encode(PORT_TYPE_10G, 0) |
7556 phy_encode(PORT_TYPE_10G, 1));
7276 } else { 7557 } else {
7277 err = fill_phy_probe_info(np, parent, info); 7558 err = fill_phy_probe_info(np, parent, info);
7278 if (err) 7559 if (err)
@@ -7733,15 +8014,16 @@ static int __devinit niu_get_invariants(struct niu *np)
7733 8014
7734 have_props = !err; 8015 have_props = !err;
7735 8016
7736 err = niu_get_and_validate_port(np);
7737 if (err)
7738 return err;
7739
7740 err = niu_init_mac_ipp_pcs_base(np); 8017 err = niu_init_mac_ipp_pcs_base(np);
7741 if (err) 8018 if (err)
7742 return err; 8019 return err;
7743 8020
7744 if (!have_props) { 8021 if (have_props) {
8022 err = niu_get_and_validate_port(np);
8023 if (err)
8024 return err;
8025
8026 } else {
7745 if (np->parent->plat_type == PLAT_TYPE_NIU) 8027 if (np->parent->plat_type == PLAT_TYPE_NIU)
7746 return -EINVAL; 8028 return -EINVAL;
7747 8029
@@ -7753,10 +8035,17 @@ static int __devinit niu_get_invariants(struct niu *np)
7753 niu_pci_vpd_fetch(np, offset); 8035 niu_pci_vpd_fetch(np, offset);
7754 nw64(ESPC_PIO_EN, 0); 8036 nw64(ESPC_PIO_EN, 0);
7755 8037
7756 if (np->flags & NIU_FLAGS_VPD_VALID) 8038 if (np->flags & NIU_FLAGS_VPD_VALID) {
7757 niu_pci_vpd_validate(np); 8039 niu_pci_vpd_validate(np);
8040 err = niu_get_and_validate_port(np);
8041 if (err)
8042 return err;
8043 }
7758 8044
7759 if (!(np->flags & NIU_FLAGS_VPD_VALID)) { 8045 if (!(np->flags & NIU_FLAGS_VPD_VALID)) {
8046 err = niu_get_and_validate_port(np);
8047 if (err)
8048 return err;
7760 err = niu_pci_probe_sprom(np); 8049 err = niu_pci_probe_sprom(np);
7761 if (err) 8050 if (err)
7762 return err; 8051 return err;