aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefano Brivio <stefano.brivio@polimi.it>2006-10-16 00:18:11 -0400
committerJeff Garzik <jeff@garzik.org>2006-12-02 00:11:58 -0500
commitf3d1fca3eb05cf6ff3879a385a15d24fbf556c57 (patch)
treef8625cb837369a8ee7755f4d0639a9abe36fd860
parent995cdaa45b1faf8f4472e4b4c6027dc685ae1b54 (diff)
[PATCH] bcm43xx: add PCI-E code
The current bcm43xx driver does not contain code to handle PCI-E interfaces such as the BCM4311 and BCM4312. This patch, originally written by Stefano Brivio adds the necessary code to enable these interfaces. Signed-off-by: Stefano Brivio <stefano.brivio@polimi.it> Signed-off-by: Larry Finger <Larry.Finger@lwfinger.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h29
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c143
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_power.c28
3 files changed, 156 insertions, 44 deletions
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 5f43d7f42af1..94dfb92fab5c 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -159,6 +159,7 @@
159 159
160/* Chipcommon registers. */ 160/* Chipcommon registers. */
161#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04 161#define BCM43xx_CHIPCOMMON_CAPABILITIES 0x04
162#define BCM43xx_CHIPCOMMON_CTL 0x28
162#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0 163#define BCM43xx_CHIPCOMMON_PLLONDELAY 0xB0
163#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4 164#define BCM43xx_CHIPCOMMON_FREFSELDELAY 0xB4
164#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8 165#define BCM43xx_CHIPCOMMON_SLOWCLKCTL 0xB8
@@ -172,6 +173,33 @@
172/* SBTOPCI2 values. */ 173/* SBTOPCI2 values. */
173#define BCM43xx_SBTOPCI2_PREFETCH 0x4 174#define BCM43xx_SBTOPCI2_PREFETCH 0x4
174#define BCM43xx_SBTOPCI2_BURST 0x8 175#define BCM43xx_SBTOPCI2_BURST 0x8
176#define BCM43xx_SBTOPCI2_MEMREAD_MULTI 0x20
177
178/* PCI-E core registers. */
179#define BCM43xx_PCIECORE_REG_ADDR 0x0130
180#define BCM43xx_PCIECORE_REG_DATA 0x0134
181#define BCM43xx_PCIECORE_MDIO_CTL 0x0128
182#define BCM43xx_PCIECORE_MDIO_DATA 0x012C
183
184/* PCI-E registers. */
185#define BCM43xx_PCIE_TLP_WORKAROUND 0x0004
186#define BCM43xx_PCIE_DLLP_LINKCTL 0x0100
187
188/* PCI-E MDIO bits. */
189#define BCM43xx_PCIE_MDIO_ST 0x40000000
190#define BCM43xx_PCIE_MDIO_WT 0x10000000
191#define BCM43xx_PCIE_MDIO_DEV 22
192#define BCM43xx_PCIE_MDIO_REG 18
193#define BCM43xx_PCIE_MDIO_TA 0x00020000
194#define BCM43xx_PCIE_MDIO_TC 0x0100
195
196/* MDIO devices. */
197#define BCM43xx_MDIO_SERDES_RX 0x1F
198
199/* SERDES RX registers. */
200#define BCM43xx_SERDES_RXTIMER 0x2
201#define BCM43xx_SERDES_CDR 0x6
202#define BCM43xx_SERDES_CDR_BW 0x7
175 203
176/* Chipcommon capabilities. */ 204/* Chipcommon capabilities. */
177#define BCM43xx_CAPABILITIES_PCTL 0x00040000 205#define BCM43xx_CAPABILITIES_PCTL 0x00040000
@@ -221,6 +249,7 @@
221#define BCM43xx_COREID_USB20_HOST 0x819 249#define BCM43xx_COREID_USB20_HOST 0x819
222#define BCM43xx_COREID_USB20_DEV 0x81a 250#define BCM43xx_COREID_USB20_DEV 0x81a
223#define BCM43xx_COREID_SDIO_HOST 0x81b 251#define BCM43xx_COREID_SDIO_HOST 0x81b
252#define BCM43xx_COREID_PCIE 0x820
224 253
225/* Core Information Registers */ 254/* Core Information Registers */
226#define BCM43xx_CIR_BASE 0xf00 255#define BCM43xx_CIR_BASE 0xf00
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index 2ffc0d5b54ee..c6bd86889f1b 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -130,6 +130,10 @@ MODULE_PARM_DESC(fwpostfix, "Postfix for .fw files. Useful for debugging.");
130 { PCI_VENDOR_ID_BROADCOM, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 130 { PCI_VENDOR_ID_BROADCOM, 0x4301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
131 /* Broadcom 4307 802.11b */ 131 /* Broadcom 4307 802.11b */
132 { PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 132 { PCI_VENDOR_ID_BROADCOM, 0x4307, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
133 /* Broadcom 4311 802.11(a)/b/g */
134 { PCI_VENDOR_ID_BROADCOM, 0x4311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
135 /* Broadcom 4312 802.11a/b/g */
136 { PCI_VENDOR_ID_BROADCOM, 0x4312, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
133 /* Broadcom 4318 802.11b/g */ 137 /* Broadcom 4318 802.11b/g */
134 { PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, 138 { PCI_VENDOR_ID_BROADCOM, 0x4318, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
135 /* Broadcom 4319 802.11a/b/g */ 139 /* Broadcom 4319 802.11a/b/g */
@@ -2600,8 +2604,9 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
2600 /* fetch sb_id_hi from core information registers */ 2604 /* fetch sb_id_hi from core information registers */
2601 sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI); 2605 sb_id_hi = bcm43xx_read32(bcm, BCM43xx_CIR_SB_ID_HI);
2602 2606
2603 core_id = (sb_id_hi & 0xFFF0) >> 4; 2607 core_id = (sb_id_hi & 0x8FF0) >> 4;
2604 core_rev = (sb_id_hi & 0xF); 2608 core_rev = (sb_id_hi & 0x7000) >> 8;
2609 core_rev |= (sb_id_hi & 0xF);
2605 core_vendor = (sb_id_hi & 0xFFFF0000) >> 16; 2610 core_vendor = (sb_id_hi & 0xFFFF0000) >> 16;
2606 2611
2607 /* if present, chipcommon is always core 0; read the chipid from it */ 2612 /* if present, chipcommon is always core 0; read the chipid from it */
@@ -2711,6 +2716,7 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
2711 core = NULL; 2716 core = NULL;
2712 switch (core_id) { 2717 switch (core_id) {
2713 case BCM43xx_COREID_PCI: 2718 case BCM43xx_COREID_PCI:
2719 case BCM43xx_COREID_PCIE:
2714 core = &bcm->core_pci; 2720 core = &bcm->core_pci;
2715 if (core->available) { 2721 if (core->available) {
2716 printk(KERN_WARNING PFX "Multiple PCI cores found.\n"); 2722 printk(KERN_WARNING PFX "Multiple PCI cores found.\n");
@@ -2749,12 +2755,12 @@ static int bcm43xx_probe_cores(struct bcm43xx_private *bcm)
2749 case 6: 2755 case 6:
2750 case 7: 2756 case 7:
2751 case 9: 2757 case 9:
2758 case 10:
2752 break; 2759 break;
2753 default: 2760 default:
2754 printk(KERN_ERR PFX "Error: Unsupported 80211 core revision %u\n", 2761 printk(KERN_WARNING PFX
2762 "Unsupported 80211 core revision %u\n",
2755 core_rev); 2763 core_rev);
2756 err = -ENODEV;
2757 goto out;
2758 } 2764 }
2759 bcm->nr_80211_available++; 2765 bcm->nr_80211_available++;
2760 core->priv = ext_80211; 2766 core->priv = ext_80211;
@@ -2868,16 +2874,11 @@ static int bcm43xx_wireless_core_init(struct bcm43xx_private *bcm,
2868 u32 sbimconfiglow; 2874 u32 sbimconfiglow;
2869 u8 limit; 2875 u8 limit;
2870 2876
2871 if (bcm->chip_rev < 5) { 2877 if (bcm->core_pci.rev <= 5 && bcm->core_pci.id != BCM43xx_COREID_PCIE) {
2872 sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW); 2878 sbimconfiglow = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);
2873 sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK; 2879 sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;
2874 sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK; 2880 sbimconfiglow &= ~ BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;
2875 if (bcm->bustype == BCM43xx_BUSTYPE_PCI) 2881 sbimconfiglow |= 0x32;
2876 sbimconfiglow |= 0x32;
2877 else if (bcm->bustype == BCM43xx_BUSTYPE_SB)
2878 sbimconfiglow |= 0x53;
2879 else
2880 assert(0);
2881 bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow); 2882 bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, sbimconfiglow);
2882 } 2883 }
2883 2884
@@ -3004,22 +3005,64 @@ static void bcm43xx_pcicore_broadcast_value(struct bcm43xx_private *bcm,
3004 3005
3005static int bcm43xx_pcicore_commit_settings(struct bcm43xx_private *bcm) 3006static int bcm43xx_pcicore_commit_settings(struct bcm43xx_private *bcm)
3006{ 3007{
3007 int err; 3008 int err = 0;
3008 struct bcm43xx_coreinfo *old_core;
3009 3009
3010 old_core = bcm->current_core; 3010 bcm->irq_savedstate = bcm43xx_interrupt_disable(bcm, BCM43xx_IRQ_ALL);
3011 err = bcm43xx_switch_core(bcm, &bcm->core_pci);
3012 if (err)
3013 goto out;
3014 3011
3015 bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000); 3012 if (bcm->core_chipcommon.available) {
3013 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
3014 if (err)
3015 goto out;
3016
3017 bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000);
3018
3019 /* this function is always called when a PCI core is mapped */
3020 err = bcm43xx_switch_core(bcm, &bcm->core_pci);
3021 if (err)
3022 goto out;
3023 } else
3024 bcm43xx_pcicore_broadcast_value(bcm, 0xfd8, 0x00000000);
3025
3026 bcm43xx_interrupt_enable(bcm, bcm->irq_savedstate);
3016 3027
3017 bcm43xx_switch_core(bcm, old_core);
3018 assert(err == 0);
3019out: 3028out:
3020 return err; 3029 return err;
3021} 3030}
3022 3031
3032static u32 bcm43xx_pcie_reg_read(struct bcm43xx_private *bcm, u32 address)
3033{
3034 bcm43xx_write32(bcm, BCM43xx_PCIECORE_REG_ADDR, address);
3035 return bcm43xx_read32(bcm, BCM43xx_PCIECORE_REG_DATA);
3036}
3037
3038static void bcm43xx_pcie_reg_write(struct bcm43xx_private *bcm, u32 address,
3039 u32 data)
3040{
3041 bcm43xx_write32(bcm, BCM43xx_PCIECORE_REG_ADDR, address);
3042 bcm43xx_write32(bcm, BCM43xx_PCIECORE_REG_DATA, data);
3043}
3044
3045static void bcm43xx_pcie_mdio_write(struct bcm43xx_private *bcm, u8 dev, u8 reg,
3046 u16 data)
3047{
3048 int i;
3049
3050 bcm43xx_write32(bcm, BCM43xx_PCIECORE_MDIO_CTL, 0x0082);
3051 bcm43xx_write32(bcm, BCM43xx_PCIECORE_MDIO_DATA, BCM43xx_PCIE_MDIO_ST |
3052 BCM43xx_PCIE_MDIO_WT | (dev << BCM43xx_PCIE_MDIO_DEV) |
3053 (reg << BCM43xx_PCIE_MDIO_REG) | BCM43xx_PCIE_MDIO_TA |
3054 data);
3055 udelay(10);
3056
3057 for (i = 0; i < 10; i++) {
3058 if (bcm43xx_read32(bcm, BCM43xx_PCIECORE_MDIO_CTL) &
3059 BCM43xx_PCIE_MDIO_TC)
3060 break;
3061 msleep(1);
3062 }
3063 bcm43xx_write32(bcm, BCM43xx_PCIECORE_MDIO_CTL, 0);
3064}
3065
3023/* Make an I/O Core usable. "core_mask" is the bitmask of the cores to enable. 3066/* Make an I/O Core usable. "core_mask" is the bitmask of the cores to enable.
3024 * To enable core 0, pass a core_mask of 1<<0 3067 * To enable core 0, pass a core_mask of 1<<0
3025 */ 3068 */
@@ -3039,7 +3082,8 @@ static int bcm43xx_setup_backplane_pci_connection(struct bcm43xx_private *bcm,
3039 if (err) 3082 if (err)
3040 goto out; 3083 goto out;
3041 3084
3042 if (bcm->core_pci.rev < 6) { 3085 if (bcm->current_core->rev < 6 ||
3086 bcm->current_core->id == BCM43xx_COREID_PCI) {
3043 value = bcm43xx_read32(bcm, BCM43xx_CIR_SBINTVEC); 3087 value = bcm43xx_read32(bcm, BCM43xx_CIR_SBINTVEC);
3044 value |= (1 << backplane_flag_nr); 3088 value |= (1 << backplane_flag_nr);
3045 bcm43xx_write32(bcm, BCM43xx_CIR_SBINTVEC, value); 3089 bcm43xx_write32(bcm, BCM43xx_CIR_SBINTVEC, value);
@@ -3057,21 +3101,46 @@ static int bcm43xx_setup_backplane_pci_connection(struct bcm43xx_private *bcm,
3057 } 3101 }
3058 } 3102 }
3059 3103
3060 value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2); 3104 if (bcm->current_core->id == BCM43xx_COREID_PCI) {
3061 value |= BCM43xx_SBTOPCI2_PREFETCH | BCM43xx_SBTOPCI2_BURST; 3105 value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2);
3062 bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value); 3106 value |= BCM43xx_SBTOPCI2_PREFETCH | BCM43xx_SBTOPCI2_BURST;
3063 3107 bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value);
3064 if (bcm->core_pci.rev < 5) { 3108
3065 value = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW); 3109 if (bcm->current_core->rev < 5) {
3066 value |= (2 << BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT) 3110 value = bcm43xx_read32(bcm, BCM43xx_CIR_SBIMCONFIGLOW);
3067 & BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK; 3111 value |= (2 << BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_SHIFT)
3068 value |= (3 << BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT) 3112 & BCM43xx_SBIMCONFIGLOW_SERVICE_TOUT_MASK;
3069 & BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK; 3113 value |= (3 << BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_SHIFT)
3070 bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, value); 3114 & BCM43xx_SBIMCONFIGLOW_REQUEST_TOUT_MASK;
3071 err = bcm43xx_pcicore_commit_settings(bcm); 3115 bcm43xx_write32(bcm, BCM43xx_CIR_SBIMCONFIGLOW, value);
3072 assert(err == 0); 3116 err = bcm43xx_pcicore_commit_settings(bcm);
3117 assert(err == 0);
3118 } else if (bcm->current_core->rev >= 11) {
3119 value = bcm43xx_read32(bcm, BCM43xx_PCICORE_SBTOPCI2);
3120 value |= BCM43xx_SBTOPCI2_MEMREAD_MULTI;
3121 bcm43xx_write32(bcm, BCM43xx_PCICORE_SBTOPCI2, value);
3122 }
3123 } else {
3124 if (bcm->current_core->rev == 0 || bcm->current_core->rev == 1) {
3125 value = bcm43xx_pcie_reg_read(bcm, BCM43xx_PCIE_TLP_WORKAROUND);
3126 value |= 0x8;
3127 bcm43xx_pcie_reg_write(bcm, BCM43xx_PCIE_TLP_WORKAROUND,
3128 value);
3129 }
3130 if (bcm->current_core->rev == 0) {
3131 bcm43xx_pcie_mdio_write(bcm, BCM43xx_MDIO_SERDES_RX,
3132 BCM43xx_SERDES_RXTIMER, 0x8128);
3133 bcm43xx_pcie_mdio_write(bcm, BCM43xx_MDIO_SERDES_RX,
3134 BCM43xx_SERDES_CDR, 0x0100);
3135 bcm43xx_pcie_mdio_write(bcm, BCM43xx_MDIO_SERDES_RX,
3136 BCM43xx_SERDES_CDR_BW, 0x1466);
3137 } else if (bcm->current_core->rev == 1) {
3138 value = bcm43xx_pcie_reg_read(bcm, BCM43xx_PCIE_DLLP_LINKCTL);
3139 value |= 0x40;
3140 bcm43xx_pcie_reg_write(bcm, BCM43xx_PCIE_DLLP_LINKCTL,
3141 value);
3142 }
3073 } 3143 }
3074
3075out_switch_back: 3144out_switch_back:
3076 err = bcm43xx_switch_core(bcm, old_core); 3145 err = bcm43xx_switch_core(bcm, old_core);
3077out: 3146out:
@@ -3676,7 +3745,7 @@ static int bcm43xx_read_phyinfo(struct bcm43xx_private *bcm)
3676 bcm->ieee->freq_band = IEEE80211_24GHZ_BAND; 3745 bcm->ieee->freq_band = IEEE80211_24GHZ_BAND;
3677 break; 3746 break;
3678 case BCM43xx_PHYTYPE_G: 3747 case BCM43xx_PHYTYPE_G:
3679 if (phy_rev > 7) 3748 if (phy_rev > 8)
3680 phy_rev_ok = 0; 3749 phy_rev_ok = 0;
3681 bcm->ieee->modulation = IEEE80211_OFDM_MODULATION | 3750 bcm->ieee->modulation = IEEE80211_OFDM_MODULATION |
3682 IEEE80211_CCK_MODULATION; 3751 IEEE80211_CCK_MODULATION;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_power.c b/drivers/net/wireless/bcm43xx/bcm43xx_power.c
index 6569da3a7a39..7e774f410953 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_power.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_power.c
@@ -153,8 +153,6 @@ int bcm43xx_pctl_init(struct bcm43xx_private *bcm)
153 int err, maxfreq; 153 int err, maxfreq;
154 struct bcm43xx_coreinfo *old_core; 154 struct bcm43xx_coreinfo *old_core;
155 155
156 if (!(bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL))
157 return 0;
158 old_core = bcm->current_core; 156 old_core = bcm->current_core;
159 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon); 157 err = bcm43xx_switch_core(bcm, &bcm->core_chipcommon);
160 if (err == -ENODEV) 158 if (err == -ENODEV)
@@ -162,11 +160,27 @@ int bcm43xx_pctl_init(struct bcm43xx_private *bcm)
162 if (err) 160 if (err)
163 goto out; 161 goto out;
164 162
165 maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1); 163 if (bcm->chip_id == 0x4321) {
166 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY, 164 if (bcm->chip_rev == 0)
167 (maxfreq * 150 + 999999) / 1000000); 165 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x03A4);
168 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY, 166 if (bcm->chip_rev == 1)
169 (maxfreq * 15 + 999999) / 1000000); 167 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_CTL, 0x00A4);
168 }
169
170 if (bcm->chipcommon_capabilities & BCM43xx_CAPABILITIES_PCTL) {
171 if (bcm->current_core->rev >= 10) {
172 /* Set Idle Power clock rate to 1Mhz */
173 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL,
174 (bcm43xx_read32(bcm, BCM43xx_CHIPCOMMON_SYSCLKCTL)
175 & 0x0000FFFF) | 0x40000);
176 } else {
177 maxfreq = bcm43xx_pctl_clockfreqlimit(bcm, 1);
178 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_PLLONDELAY,
179 (maxfreq * 150 + 999999) / 1000000);
180 bcm43xx_write32(bcm, BCM43xx_CHIPCOMMON_FREFSELDELAY,
181 (maxfreq * 15 + 999999) / 1000000);
182 }
183 }
170 184
171 err = bcm43xx_switch_core(bcm, old_core); 185 err = bcm43xx_switch_core(bcm, old_core);
172 assert(err == 0); 186 assert(err == 0);