Pull x86 platform driver fixes from Ilpo Järvinen: - Intel PMC GBE LTR regression - P2SB / PCI deadlock fix * tag 'platform-drivers-x86-v6.7-6' of git://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86: platform/x86/intel/pmc: Move GBE LTR ignore to suspend callback platform/x86/intel/pmc: Allow reenabling LTRs platform/x86/intel/pmc: Add suspend callback platform/x86: p2sb: Allow p2sb_bar() calls during PCI device probe
Unified · Split
| @@ -314,16 +314,13 @@ int adl_core_init(struct pmc_dev *pmcdev) · 313 unmodified lines | ||
| 314 | 314 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; |
| 315 | 315 | int ret; |
| 316 | 316 | |
| 317 | + pmcdev->suspend = cnl_suspend; | |
| 318 | + pmcdev->resume = cnl_resume; | |
| 319 | + | |
| 317 | 320 | pmc->map = &adl_reg_map; |
| 318 | 321 | ret = get_primary_reg_base(pmc); |
| 319 | 322 | if (ret) |
| 320 | 323 | return ret; |
| 321 | 324 | |
| 322 | − /* Due to a hardware limitation, the GBE LTR blocks PC10 | |
| 323 | − * when a cable is attached. Tell the PMC to ignore it. | |
| 324 | − */ | |
| 325 | − dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | |
| 326 | − pmc_core_send_ltr_ignore(pmcdev, 3); | |
| 327 | − | |
| 328 | 325 | return 0; |
| 329 | 326 | } |
| @@ -204,21 +204,35 @@ const struct pmc_reg_map cnp_reg_map = { · 203 unmodified lines | ||
| 204 | 204 | .etr3_offset = ETR3_OFFSET, |
| 205 | 205 | }; |
| 206 | 206 | |
| 207 | +void cnl_suspend(struct pmc_dev *pmcdev) | |
| 208 | +{ | |
| 209 | + /* | |
| 210 | + * Due to a hardware limitation, the GBE LTR blocks PC10 | |
| 211 | + * when a cable is attached. To unblock PC10 during suspend, | |
| 212 | + * tell the PMC to ignore it. | |
| 213 | + */ | |
| 214 | + pmc_core_send_ltr_ignore(pmcdev, 3, 1); | |
| 215 | +} | |
| 216 | + | |
| 217 | +int cnl_resume(struct pmc_dev *pmcdev) | |
| 218 | +{ | |
| 219 | + pmc_core_send_ltr_ignore(pmcdev, 3, 0); | |
| 220 | + | |
| 221 | + return pmc_core_resume_common(pmcdev); | |
| 222 | +} | |
| 223 | + | |
| 207 | 224 | int cnp_core_init(struct pmc_dev *pmcdev) |
| 208 | 225 | { |
| 209 | 226 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; |
| 210 | 227 | int ret; |
| 211 | 228 | |
| 229 | + pmcdev->suspend = cnl_suspend; | |
| 230 | + pmcdev->resume = cnl_resume; | |
| 231 | + | |
| 212 | 232 | pmc->map = &cnp_reg_map; |
| 213 | 233 | ret = get_primary_reg_base(pmc); |
| 214 | 234 | if (ret) |
| 215 | 235 | return ret; |
| 216 | 236 | |
| 217 | − /* Due to a hardware limitation, the GBE LTR blocks PC10 | |
| 218 | − * when a cable is attached. Tell the PMC to ignore it. | |
| 219 | − */ | |
| 220 | − dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | |
| 221 | − pmc_core_send_ltr_ignore(pmcdev, 3); | |
| 222 | − | |
| 223 | 237 | return 0; |
| 224 | 238 | } |
| @@ -460,7 +460,7 @@ out_unlock: · 459 unmodified lines | ||
| 460 | 460 | } |
| 461 | 461 | DEFINE_SHOW_ATTRIBUTE(pmc_core_pll); |
| 462 | 462 | |
| 463 | −int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value) | |
| 463 | +int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore) | |
| 464 | 464 | { |
| 465 | 465 | struct pmc *pmc; |
| 466 | 466 | const struct pmc_reg_map *map; |
| @@ -498,7 +498,10 @@ int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value) · 31 unmodified lines | ||
| 498 | 498 | mutex_lock(&pmcdev->lock); |
| 499 | 499 | |
| 500 | 500 | reg = pmc_core_reg_read(pmc, map->ltr_ignore_offset); |
| 501 | − reg |= BIT(ltr_index); | |
| 501 | + if (ignore) | |
| 502 | + reg |= BIT(ltr_index); | |
| 503 | + else | |
| 504 | + reg &= ~BIT(ltr_index); | |
| 502 | 505 | pmc_core_reg_write(pmc, map->ltr_ignore_offset, reg); |
| 503 | 506 | |
| 504 | 507 | mutex_unlock(&pmcdev->lock); |
| @@ -521,7 +524,7 @@ static ssize_t pmc_core_ltr_ignore_write(struct file *file, · 16 unmodified lines | ||
| 521 | 524 | if (err) |
| 522 | 525 | return err; |
| 523 | 526 | |
| 524 | − err = pmc_core_send_ltr_ignore(pmcdev, value); | |
| 527 | + err = pmc_core_send_ltr_ignore(pmcdev, value, 1); | |
| 525 | 528 | |
| 526 | 529 | return err == 0 ? count : err; |
| 527 | 530 | } |
| @@ -1279,6 +1282,9 @@ static __maybe_unused int pmc_core_suspend(struct device *dev) · 751 unmodified lines | ||
| 1279 | 1282 | struct pmc_dev *pmcdev = dev_get_drvdata(dev); |
| 1280 | 1283 | struct pmc *pmc = pmcdev->pmcs[PMC_IDX_MAIN]; |
| 1281 | 1284 | |
| 1285 | + if (pmcdev->suspend) | |
| 1286 | + pmcdev->suspend(pmcdev); | |
| 1287 | + | |
| 1282 | 1288 | /* Check if the syspend will actually use S0ix */ |
| 1283 | 1289 | if (pm_suspend_via_firmware()) |
| 1284 | 1290 | return 0; |
| @@ -363,6 +363,7 @@ struct pmc { · 362 unmodified lines | ||
| 363 | 363 | * @s0ix_counter: S0ix residency (step adjusted) |
| 364 | 364 | * @num_lpm_modes: Count of enabled modes |
| 365 | 365 | * @lpm_en_modes: Array of enabled modes from lowest to highest priority |
| 366 | + * @suspend: Function to perform platform specific suspend | |
| 366 | 367 | * @resume: Function to perform platform specific resume |
| 367 | 368 | * |
| 368 | 369 | * pmc_dev contains info about power management controller device. |
| @@ -379,6 +380,7 @@ struct pmc_dev { · 10 unmodified lines | ||
| 379 | 380 | u64 s0ix_counter; |
| 380 | 381 | int num_lpm_modes; |
| 381 | 382 | int lpm_en_modes[LPM_MAX_NUM_MODES]; |
| 383 | + void (*suspend)(struct pmc_dev *pmcdev); | |
| 382 | 384 | int (*resume)(struct pmc_dev *pmcdev); |
| 383 | 385 | |
| 384 | 386 | bool has_die_c6; |
| @@ -486,7 +488,7 @@ extern const struct pmc_bit_map *mtl_ioem_lpm_maps[]; · 101 unmodified lines | ||
| 486 | 488 | extern const struct pmc_reg_map mtl_ioem_reg_map; |
| 487 | 489 | |
| 488 | 490 | extern void pmc_core_get_tgl_lpm_reqs(struct platform_device *pdev); |
| 489 | −extern int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value); | |
| 491 | +int pmc_core_send_ltr_ignore(struct pmc_dev *pmcdev, u32 value, int ignore); | |
| 490 | 492 | |
| 491 | 493 | int pmc_core_resume_common(struct pmc_dev *pmcdev); |
| 492 | 494 | int get_primary_reg_base(struct pmc *pmc); |
| @@ -500,6 +502,9 @@ int tgl_core_init(struct pmc_dev *pmcdev); · 7 unmodified lines | ||
| 500 | 502 | int adl_core_init(struct pmc_dev *pmcdev); |
| 501 | 503 | int mtl_core_init(struct pmc_dev *pmcdev); |
| 502 | 504 | |
| 505 | +void cnl_suspend(struct pmc_dev *pmcdev); | |
| 506 | +int cnl_resume(struct pmc_dev *pmcdev); | |
| 507 | + | |
| 503 | 508 | #define pmc_for_each_mode(i, mode, pmcdev) \ |
| 504 | 509 | for (i = 0, mode = pmcdev->lpm_en_modes[i]; \ |
| 505 | 510 | i < pmcdev->num_lpm_modes; \ |
| @@ -979,6 +979,8 @@ static void mtl_d3_fixup(void) · 978 unmodified lines | ||
| 979 | 979 | static int mtl_resume(struct pmc_dev *pmcdev) |
| 980 | 980 | { |
| 981 | 981 | mtl_d3_fixup(); |
| 982 | + pmc_core_send_ltr_ignore(pmcdev, 3, 0); | |
| 983 | + | |
| 982 | 984 | return pmc_core_resume_common(pmcdev); |
| 983 | 985 | } |
| 984 | 986 | |
| @@ -989,6 +991,7 @@ int mtl_core_init(struct pmc_dev *pmcdev) · 4 unmodified lines | ||
| 989 | 991 | |
| 990 | 992 | mtl_d3_fixup(); |
| 991 | 993 | |
| 994 | + pmcdev->suspend = cnl_suspend; | |
| 992 | 995 | pmcdev->resume = mtl_resume; |
| 993 | 996 | |
| 994 | 997 | pmcdev->regmap_list = mtl_pmc_info_list; |
| @@ -1002,11 +1005,5 @@ int mtl_core_init(struct pmc_dev *pmcdev) · 7 unmodified lines | ||
| 1002 | 1005 | return ret; |
| 1003 | 1006 | } |
| 1004 | 1007 | |
| 1005 | − /* Due to a hardware limitation, the GBE LTR blocks PC10 | |
| 1006 | − * when a cable is attached. Tell the PMC to ignore it. | |
| 1007 | − */ | |
| 1008 | − dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | |
| 1009 | − pmc_core_send_ltr_ignore(pmcdev, 3); | |
| 1010 | − | |
| 1011 | 1008 | return 0; |
| 1012 | 1009 | } |
| @@ -259,16 +259,15 @@ int tgl_core_init(struct pmc_dev *pmcdev) · 258 unmodified lines | ||
| 259 | 259 | int ret; |
| 260 | 260 | |
| 261 | 261 | pmc->map = &tgl_reg_map; |
| 262 | + | |
| 263 | + pmcdev->suspend = cnl_suspend; | |
| 264 | + pmcdev->resume = cnl_resume; | |
| 265 | + | |
| 262 | 266 | ret = get_primary_reg_base(pmc); |
| 263 | 267 | if (ret) |
| 264 | 268 | return ret; |
| 265 | 269 | |
| 266 | 270 | pmc_core_get_tgl_lpm_reqs(pmcdev->pdev); |
| 267 | − /* Due to a hardware limitation, the GBE LTR blocks PC10 | |
| 268 | − * when a cable is attached. Tell the PMC to ignore it. | |
| 269 | − */ | |
| 270 | − dev_dbg(&pmcdev->pdev->dev, "ignoring GBE LTR\n"); | |
| 271 | − pmc_core_send_ltr_ignore(pmcdev, 3); | |
| 272 | 271 | |
| 273 | 272 | return 0; |
| 274 | 273 | } |
| @@ -26,6 +26,21 @@ static const struct x86_cpu_id p2sb_cpu_ids[] = { · 25 unmodified lines | ||
| 26 | 26 | {} |
| 27 | 27 | }; |
| 28 | 28 | |
| 29 | +/* | |
| 30 | + * Cache BAR0 of P2SB device functions 0 to 7. | |
| 31 | + * TODO: The constant 8 is the number of functions that PCI specification | |
| 32 | + * defines. Same definitions exist tree-wide. Unify this definition and | |
| 33 | + * the other definitions then move to include/uapi/linux/pci.h. | |
| 34 | + */ | |
| 35 | +#define NR_P2SB_RES_CACHE 8 | |
| 36 | + | |
| 37 | +struct p2sb_res_cache { | |
| 38 | + u32 bus_dev_id; | |
| 39 | + struct resource res; | |
| 40 | +}; | |
| 41 | + | |
| 42 | +static struct p2sb_res_cache p2sb_resources[NR_P2SB_RES_CACHE]; | |
| 43 | + | |
| 29 | 44 | static int p2sb_get_devfn(unsigned int *devfn) |
| 30 | 45 | { |
| 31 | 46 | unsigned int fn = P2SB_DEVFN_DEFAULT; |
| @@ -39,8 +54,16 @@ static int p2sb_get_devfn(unsigned int *devfn) · 7 unmodified lines | ||
| 39 | 54 | return 0; |
| 40 | 55 | } |
| 41 | 56 | |
| 57 | +static bool p2sb_valid_resource(struct resource *res) | |
| 58 | +{ | |
| 59 | + if (res->flags) | |
| 60 | + return true; | |
| 61 | + | |
| 62 | + return false; | |
| 63 | +} | |
| 64 | + | |
| 42 | 65 | /* Copy resource from the first BAR of the device in question */ |
| 43 | −static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) | |
| 66 | +static void p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) | |
| 44 | 67 | { |
| 45 | 68 | struct resource *bar0 = &pdev->resource[0]; |
| 46 | 69 | |
| @@ -56,47 +79,64 @@ static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) · 9 unmodified lines | ||
| 56 | 79 | mem->end = bar0->end; |
| 57 | 80 | mem->flags = bar0->flags; |
| 58 | 81 | mem->desc = bar0->desc; |
| 59 | − | |
| 60 | − return 0; | |
| 61 | 82 | } |
| 62 | 83 | |
| 63 | −static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem) | |
| 84 | +static void p2sb_scan_and_cache_devfn(struct pci_bus *bus, unsigned int devfn) | |
| 64 | 85 | { |
| 86 | + struct p2sb_res_cache *cache = &p2sb_resources[PCI_FUNC(devfn)]; | |
| 65 | 87 | struct pci_dev *pdev; |
| 66 | − int ret; | |
| 67 | 88 | |
| 68 | 89 | pdev = pci_scan_single_device(bus, devfn); |
| 69 | 90 | if (!pdev) |
| 70 | − return -ENODEV; | |
| 91 | + return; | |
| 71 | 92 | |
| 72 | − ret = p2sb_read_bar0(pdev, mem); | |
| 93 | + p2sb_read_bar0(pdev, &cache->res); | |
| 94 | + cache->bus_dev_id = bus->dev.id; | |
| 73 | 95 | |
| 74 | 96 | pci_stop_and_remove_bus_device(pdev); |
| 75 | − return ret; | |
| 97 | + return; | |
| 76 | 98 | } |
| 77 | 99 | |
| 78 | −/** | |
| 79 | − * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR | |
| 80 | − * @bus: PCI bus to communicate with | |
| 81 | − * @devfn: PCI slot and function to communicate with | |
| 82 | − * @mem: memory resource to be filled in | |
| 83 | − * | |
| 84 | − * The BIOS prevents the P2SB device from being enumerated by the PCI | |
| 85 | − * subsystem, so we need to unhide and hide it back to lookup the BAR. | |
| 86 | − * | |
| 87 | − * if @bus is NULL, the bus 0 in domain 0 will be used. | |
| 88 | − * If @devfn is 0, it will be replaced by devfn of the P2SB device. | |
| 89 | − * | |
| 90 | − * Caller must provide a valid pointer to @mem. | |
| 91 | − * | |
| 92 | − * Locking is handled by pci_rescan_remove_lock mutex. | |
| 93 | − * | |
| 94 | − * Return: | |
| 95 | − * 0 on success or appropriate errno value on error. | |
| 96 | − */ | |
| 97 | −int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) | |
| 100 | +static int p2sb_scan_and_cache(struct pci_bus *bus, unsigned int devfn) | |
| 101 | +{ | |
| 102 | + unsigned int slot, fn; | |
| 103 | + | |
| 104 | + if (PCI_FUNC(devfn) == 0) { | |
| 105 | + /* | |
| 106 | + * When function number of the P2SB device is zero, scan it and | |
| 107 | + * other function numbers, and if devices are available, cache | |
| 108 | + * their BAR0s. | |
| 109 | + */ | |
| 110 | + slot = PCI_SLOT(devfn); | |
| 111 | + for (fn = 0; fn < NR_P2SB_RES_CACHE; fn++) | |
| 112 | + p2sb_scan_and_cache_devfn(bus, PCI_DEVFN(slot, fn)); | |
| 113 | + } else { | |
| 114 | + /* Scan the P2SB device and cache its BAR0 */ | |
| 115 | + p2sb_scan_and_cache_devfn(bus, devfn); | |
| 116 | + } | |
| 117 | + | |
| 118 | + if (!p2sb_valid_resource(&p2sb_resources[PCI_FUNC(devfn)].res)) | |
| 119 | + return -ENOENT; | |
| 120 | + | |
| 121 | + return 0; | |
| 122 | +} | |
| 123 | + | |
| 124 | +static struct pci_bus *p2sb_get_bus(struct pci_bus *bus) | |
| 125 | +{ | |
| 126 | + static struct pci_bus *p2sb_bus; | |
| 127 | + | |
| 128 | + bus = bus ?: p2sb_bus; | |
| 129 | + if (bus) | |
| 130 | + return bus; | |
| 131 | + | |
| 132 | + /* Assume P2SB is on the bus 0 in domain 0 */ | |
| 133 | + p2sb_bus = pci_find_bus(0, 0); | |
| 134 | + return p2sb_bus; | |
| 135 | +} | |
| 136 | + | |
| 137 | +static int p2sb_cache_resources(void) | |
| 98 | 138 | { |
| 99 | − struct pci_dev *pdev_p2sb; | |
| 139 | + struct pci_bus *bus; | |
| 100 | 140 | unsigned int devfn_p2sb; |
| 101 | 141 | u32 value = P2SBC_HIDE; |
| 102 | 142 | int ret; |
| @@ -106,8 +146,9 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) · 3 unmodified lines | ||
| 106 | 146 | if (ret) |
| 107 | 147 | return ret; |
| 108 | 148 | |
| 109 | − /* if @bus is NULL, use bus 0 in domain 0 */ | |
| 110 | − bus = bus ?: pci_find_bus(0, 0); | |
| 149 | + bus = p2sb_get_bus(NULL); | |
| 150 | + if (!bus) | |
| 151 | + return -ENODEV; | |
| 111 | 152 | |
| 112 | 153 | /* |
| 113 | 154 | * Prevent concurrent PCI bus scan from seeing the P2SB device and |
| @@ -115,17 +156,16 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) · 1 unmodified line | ||
| 115 | 156 | */ |
| 116 | 157 | pci_lock_rescan_remove(); |
| 117 | 158 | |
| 118 | − /* Unhide the P2SB device, if needed */ | |
| 159 | + /* | |
| 160 | + * The BIOS prevents the P2SB device from being enumerated by the PCI | |
| 161 | + * subsystem, so we need to unhide and hide it back to lookup the BAR. | |
| 162 | + * Unhide the P2SB device here, if needed. | |
| 163 | + */ | |
| 119 | 164 | pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value); |
| 120 | 165 | if (value & P2SBC_HIDE) |
| 121 | 166 | pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0); |
| 122 | 167 | |
| 123 | − pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb); | |
| 124 | − if (devfn) | |
| 125 | − ret = p2sb_scan_and_read(bus, devfn, mem); | |
| 126 | − else | |
| 127 | − ret = p2sb_read_bar0(pdev_p2sb, mem); | |
| 128 | − pci_stop_and_remove_bus_device(pdev_p2sb); | |
| 168 | + ret = p2sb_scan_and_cache(bus, devfn_p2sb); | |
| 129 | 169 | |
| 130 | 170 | /* Hide the P2SB device, if it was hidden */ |
| 131 | 171 | if (value & P2SBC_HIDE) |
| @@ -133,12 +173,62 @@ int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) · 1 unmodified line | ||
| 133 | 173 | |
| 134 | 174 | pci_unlock_rescan_remove(); |
| 135 | 175 | |
| 136 | − if (ret) | |
| 137 | − return ret; | |
| 176 | + return ret; | |
| 177 | +} | |
| 178 | + | |
| 179 | +/** | |
| 180 | + * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR | |
| 181 | + * @bus: PCI bus to communicate with | |
| 182 | + * @devfn: PCI slot and function to communicate with | |
| 183 | + * @mem: memory resource to be filled in | |
| 184 | + * | |
| 185 | + * If @bus is NULL, the bus 0 in domain 0 will be used. | |
| 186 | + * If @devfn is 0, it will be replaced by devfn of the P2SB device. | |
| 187 | + * | |
| 188 | + * Caller must provide a valid pointer to @mem. | |
| 189 | + * | |
| 190 | + * Return: | |
| 191 | + * 0 on success or appropriate errno value on error. | |
| 192 | + */ | |
| 193 | +int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) | |
| 194 | +{ | |
| 195 | + struct p2sb_res_cache *cache; | |
| 196 | + int ret; | |
| 138 | 197 | |
| 139 | − if (mem->flags == 0) | |
| 198 | + bus = p2sb_get_bus(bus); | |
| 199 | + if (!bus) | |
| 140 | 200 | return -ENODEV; |
| 141 | 201 | |
| 202 | + if (!devfn) { | |
| 203 | + ret = p2sb_get_devfn(&devfn); | |
| 204 | + if (ret) | |
| 205 | + return ret; | |
| 206 | + } | |
| 207 | + | |
| 208 | + cache = &p2sb_resources[PCI_FUNC(devfn)]; | |
| 209 | + if (cache->bus_dev_id != bus->dev.id) | |
| 210 | + return -ENODEV; | |
| 211 | + | |
| 212 | + if (!p2sb_valid_resource(&cache->res)) | |
| 213 | + return -ENOENT; | |
| 214 | + | |
| 215 | + memcpy(mem, &cache->res, sizeof(*mem)); | |
| 142 | 216 | return 0; |
| 143 | 217 | } |
| 144 | 218 | EXPORT_SYMBOL_GPL(p2sb_bar); |
| 219 | + | |
| 220 | +static int __init p2sb_fs_init(void) | |
| 221 | +{ | |
| 222 | + p2sb_cache_resources(); | |
| 223 | + return 0; | |
| 224 | +} | |
| 225 | + | |
| 226 | +/* | |
| 227 | + * pci_rescan_remove_lock to avoid access to unhidden P2SB devices can | |
| 228 | + * not be locked in sysfs pci bus rescan path because of deadlock. To | |
| 229 | + * avoid the deadlock, access to P2SB devices with the lock at an early | |
| 230 | + * step in kernel initialization and cache required resources. This | |
| 231 | + * should happen after subsys_initcall which initializes PCI subsystem | |
| 232 | + * and before device_initcall which requires P2SB resources. | |
| 233 | + */ | |
| 234 | +fs_initcall(p2sb_fs_init); | |