|
10 | 10 | */ |
11 | 11 |
|
12 | 12 | #include <linux/aperture.h> |
| 13 | +#include <linux/bitfield.h> |
| 14 | +#include <linux/delay.h> |
13 | 15 | #include <linux/errno.h> |
14 | 16 | #include <linux/init.h> |
| 17 | +#include <linux/iopoll.h> |
| 18 | +#include <linux/kbd_kern.h> |
15 | 19 | #include <linux/kernel.h> |
16 | 20 | #include <linux/mfd/core.h> |
17 | 21 | #include <linux/module.h> |
18 | 22 | #include <linux/pci.h> |
19 | 23 | #include <linux/pci_ids.h> |
20 | 24 | #include <linux/platform_data/simplefb.h> |
21 | 25 | #include <linux/platform_device.h> |
| 26 | +#include <linux/stop_machine.h> |
| 27 | +#include <linux/vt_kern.h> |
22 | 28 |
|
23 | 29 | /* LS2K BMC resources */ |
24 | 30 | #define LS2K_DISPLAY_RES_START (SZ_16M + SZ_2M) |
|
29 | 35 | #define LS2K_IPMI3_RES_START (LS2K_IPMI2_RES_START + LS2K_IPMI_RES_SIZE) |
30 | 36 | #define LS2K_IPMI4_RES_START (LS2K_IPMI3_RES_START + LS2K_IPMI_RES_SIZE) |
31 | 37 |
|
| 38 | +#define LS7A_PCI_CFG_SIZE 0x100 |
| 39 | + |
| 40 | +/* LS7A bridge registers */ |
| 41 | +#define LS7A_PCIE_PORT_CTL0 0x0 |
| 42 | +#define LS7A_PCIE_PORT_STS1 0xC |
| 43 | +#define LS7A_GEN2_CTL 0x80C |
| 44 | +#define LS7A_SYMBOL_TIMER 0x71C |
| 45 | + |
| 46 | +/* Bits of LS7A_PCIE_PORT_CTL0 */ |
| 47 | +#define LS2K_BMC_PCIE_LTSSM_ENABLE BIT(3) |
| 48 | + |
| 49 | +/* Bits of LS7A_PCIE_PORT_STS1 */ |
| 50 | +#define LS2K_BMC_PCIE_LTSSM_STS GENMASK(5, 0) |
| 51 | +#define LS2K_BMC_PCIE_CONNECTED 0x11 |
| 52 | + |
| 53 | +#define LS2K_BMC_PCIE_DELAY_US 1000 |
| 54 | +#define LS2K_BMC_PCIE_TIMEOUT_US 1000000 |
| 55 | + |
| 56 | +/* Bits of LS7A_GEN2_CTL */ |
| 57 | +#define LS7A_GEN2_SPEED_CHANG BIT(17) |
| 58 | +#define LS7A_CONF_PHY_TX BIT(18) |
| 59 | + |
| 60 | +/* Bits of LS7A_SYMBOL_TIMER */ |
| 61 | +#define LS7A_MASK_LEN_MATCH BIT(26) |
| 62 | + |
| 63 | +/* Interval between interruptions */ |
| 64 | +#define LS2K_BMC_INT_INTERVAL (60 * HZ) |
| 65 | + |
| 66 | +/* Maximum time to wait for U-Boot and DDR to be ready with ms. */ |
| 67 | +#define LS2K_BMC_RESET_WAIT_TIME 10000 |
| 68 | + |
| 69 | +/* It's an experience value */ |
| 70 | +#define LS7A_BAR0_CHECK_MAX_TIMES 2000 |
| 71 | + |
| 72 | +#define PCI_REG_STRIDE 0x4 |
| 73 | + |
| 74 | +#define LS2K_BMC_RESET_GPIO 14 |
| 75 | +#define LOONGSON_GPIO_REG_BASE 0x1FE00500 |
| 76 | +#define LOONGSON_GPIO_REG_SIZE 0x18 |
| 77 | +#define LOONGSON_GPIO_OEN 0x0 |
| 78 | +#define LOONGSON_GPIO_FUNC 0x4 |
| 79 | +#define LOONGSON_GPIO_INTPOL 0x10 |
| 80 | +#define LOONGSON_GPIO_INTEN 0x14 |
| 81 | + |
| 82 | +#define LOONGSON_IO_INT_BASE 16 |
| 83 | +#define LS2K_BMC_RESET_GPIO_INT_VEC (LS2K_BMC_RESET_GPIO % 8) |
| 84 | +#define LS2K_BMC_RESET_GPIO_GSI (LOONGSON_IO_INT_BASE + LS2K_BMC_RESET_GPIO_INT_VEC) |
| 85 | + |
32 | 86 | enum { |
33 | 87 | LS2K_BMC_DISPLAY, |
34 | 88 | LS2K_BMC_IPMI0, |
@@ -95,6 +149,278 @@ static struct mfd_cell ls2k_bmc_cells[] = { |
95 | 149 | }, |
96 | 150 | }; |
97 | 151 |
|
| 152 | +/* Index of the BMC PCI configuration space to be restored at BMC reset. */ |
| 153 | +struct ls2k_bmc_pci_data { |
| 154 | + u32 pci_command; |
| 155 | + u32 base_address0; |
| 156 | + u32 interrupt_line; |
| 157 | +}; |
| 158 | + |
| 159 | +/* Index of the parent PCI configuration space to be restored at BMC reset. */ |
| 160 | +struct ls2k_bmc_bridge_pci_data { |
| 161 | + u32 pci_command; |
| 162 | + u32 base_address[6]; |
| 163 | + u32 rom_addreess; |
| 164 | + u32 interrupt_line; |
| 165 | + u32 msi_hi; |
| 166 | + u32 msi_lo; |
| 167 | + u32 devctl; |
| 168 | + u32 linkcap; |
| 169 | + u32 linkctl_sts; |
| 170 | + u32 symbol_timer; |
| 171 | + u32 gen2_ctrl; |
| 172 | +}; |
| 173 | + |
| 174 | +struct ls2k_bmc_ddata { |
| 175 | + struct device *dev; |
| 176 | + struct work_struct bmc_reset_work; |
| 177 | + struct ls2k_bmc_pci_data bmc_pci_data; |
| 178 | + struct ls2k_bmc_bridge_pci_data bridge_pci_data; |
| 179 | +}; |
| 180 | + |
| 181 | +static bool ls2k_bmc_bar0_addr_is_set(struct pci_dev *pdev) |
| 182 | +{ |
| 183 | + u32 addr; |
| 184 | + |
| 185 | + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &addr); |
| 186 | + |
| 187 | + return addr & PCI_BASE_ADDRESS_MEM_MASK ? true : false; |
| 188 | +} |
| 189 | + |
| 190 | +static bool ls2k_bmc_pcie_is_connected(struct pci_dev *parent, struct ls2k_bmc_ddata *ddata) |
| 191 | +{ |
| 192 | + void __iomem *base; |
| 193 | + int val, ret; |
| 194 | + |
| 195 | + base = pci_iomap(parent, 0, LS7A_PCI_CFG_SIZE); |
| 196 | + if (!base) |
| 197 | + return false; |
| 198 | + |
| 199 | + val = readl(base + LS7A_PCIE_PORT_CTL0); |
| 200 | + writel(val | LS2K_BMC_PCIE_LTSSM_ENABLE, base + LS7A_PCIE_PORT_CTL0); |
| 201 | + |
| 202 | + ret = readl_poll_timeout_atomic(base + LS7A_PCIE_PORT_STS1, val, |
| 203 | + (val & LS2K_BMC_PCIE_LTSSM_STS) == LS2K_BMC_PCIE_CONNECTED, |
| 204 | + LS2K_BMC_PCIE_DELAY_US, LS2K_BMC_PCIE_TIMEOUT_US); |
| 205 | + if (ret) { |
| 206 | + pci_iounmap(parent, base); |
| 207 | + dev_err(ddata->dev, "PCI-E training failed status=0x%x\n", val); |
| 208 | + return false; |
| 209 | + } |
| 210 | + |
| 211 | + pci_iounmap(parent, base); |
| 212 | + return true; |
| 213 | +} |
| 214 | + |
| 215 | +static void ls2k_bmc_restore_bridge_pci_data(struct pci_dev *parent, struct ls2k_bmc_ddata *ddata) |
| 216 | +{ |
| 217 | + int base, i = 0; |
| 218 | + |
| 219 | + pci_write_config_dword(parent, PCI_COMMAND, ddata->bridge_pci_data.pci_command); |
| 220 | + |
| 221 | + for (base = PCI_BASE_ADDRESS_0; base <= PCI_BASE_ADDRESS_5; base += PCI_REG_STRIDE, i++) |
| 222 | + pci_write_config_dword(parent, base, ddata->bridge_pci_data.base_address[i]); |
| 223 | + |
| 224 | + pci_write_config_dword(parent, PCI_ROM_ADDRESS, ddata->bridge_pci_data.rom_addreess); |
| 225 | + pci_write_config_dword(parent, PCI_INTERRUPT_LINE, ddata->bridge_pci_data.interrupt_line); |
| 226 | + |
| 227 | + pci_write_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_LO, |
| 228 | + ddata->bridge_pci_data.msi_lo); |
| 229 | + pci_write_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_HI, |
| 230 | + ddata->bridge_pci_data.msi_hi); |
| 231 | + pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_DEVCTL, |
| 232 | + ddata->bridge_pci_data.devctl); |
| 233 | + pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCAP, |
| 234 | + ddata->bridge_pci_data.linkcap); |
| 235 | + pci_write_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCTL, |
| 236 | + ddata->bridge_pci_data.linkctl_sts); |
| 237 | + |
| 238 | + pci_write_config_dword(parent, LS7A_GEN2_CTL, ddata->bridge_pci_data.gen2_ctrl); |
| 239 | + pci_write_config_dword(parent, LS7A_SYMBOL_TIMER, ddata->bridge_pci_data.symbol_timer); |
| 240 | +} |
| 241 | + |
| 242 | +static int ls2k_bmc_recover_pci_data(void *data) |
| 243 | +{ |
| 244 | + struct ls2k_bmc_ddata *ddata = data; |
| 245 | + struct pci_dev *pdev = to_pci_dev(ddata->dev); |
| 246 | + struct pci_dev *parent = pdev->bus->self; |
| 247 | + u32 i; |
| 248 | + |
| 249 | + /* |
| 250 | + * Clear the bus, io and mem resources of the PCI-E bridge to zero, so that |
| 251 | + * the processor can not access the LS2K PCI-E port, to avoid crashing due to |
| 252 | + * the lack of return signal from accessing the LS2K PCI-E port. |
| 253 | + */ |
| 254 | + pci_write_config_dword(parent, PCI_BASE_ADDRESS_2, 0); |
| 255 | + pci_write_config_dword(parent, PCI_BASE_ADDRESS_3, 0); |
| 256 | + pci_write_config_dword(parent, PCI_BASE_ADDRESS_4, 0); |
| 257 | + |
| 258 | + /* |
| 259 | + * When the LS2K BMC is reset, the LS7A PCI-E port is also reset, and its PCI |
| 260 | + * BAR0 register is cleared. Due to the time gap between the GPIO interrupt |
| 261 | + * generation and the LS2K BMC reset, the LS7A PCI BAR0 register is read to |
| 262 | + * determine whether the reset has begun. |
| 263 | + */ |
| 264 | + for (i = LS7A_BAR0_CHECK_MAX_TIMES; i > 0 ; i--) { |
| 265 | + if (!ls2k_bmc_bar0_addr_is_set(parent)) |
| 266 | + break; |
| 267 | + mdelay(1); |
| 268 | + }; |
| 269 | + |
| 270 | + if (i == 0) |
| 271 | + return false; |
| 272 | + |
| 273 | + ls2k_bmc_restore_bridge_pci_data(parent, ddata); |
| 274 | + |
| 275 | + /* Check if PCI-E is connected */ |
| 276 | + if (!ls2k_bmc_pcie_is_connected(parent, ddata)) |
| 277 | + return false; |
| 278 | + |
| 279 | + /* Waiting for U-Boot and DDR ready */ |
| 280 | + mdelay(LS2K_BMC_RESET_WAIT_TIME); |
| 281 | + if (!ls2k_bmc_bar0_addr_is_set(parent)) |
| 282 | + return false; |
| 283 | + |
| 284 | + /* Restore LS2K BMC PCI-E config data */ |
| 285 | + pci_write_config_dword(pdev, PCI_COMMAND, ddata->bmc_pci_data.pci_command); |
| 286 | + pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, ddata->bmc_pci_data.base_address0); |
| 287 | + pci_write_config_dword(pdev, PCI_INTERRUPT_LINE, ddata->bmc_pci_data.interrupt_line); |
| 288 | + |
| 289 | + return 0; |
| 290 | +} |
| 291 | + |
| 292 | +static void ls2k_bmc_events_fn(struct work_struct *work) |
| 293 | +{ |
| 294 | + struct ls2k_bmc_ddata *ddata = container_of(work, struct ls2k_bmc_ddata, bmc_reset_work); |
| 295 | + |
| 296 | + /* |
| 297 | + * The PCI-E is lost when the BMC resets, at which point access to the PCI-E |
| 298 | + * from other CPUs is suspended to prevent a crash. |
| 299 | + */ |
| 300 | + stop_machine(ls2k_bmc_recover_pci_data, ddata, NULL); |
| 301 | + |
| 302 | + if (IS_ENABLED(CONFIG_VT)) { |
| 303 | + /* Re-push the display due to previous PCI-E loss. */ |
| 304 | + set_console(vt_move_to_console(MAX_NR_CONSOLES - 1, 1)); |
| 305 | + } |
| 306 | +} |
| 307 | + |
| 308 | +static irqreturn_t ls2k_bmc_interrupt(int irq, void *arg) |
| 309 | +{ |
| 310 | + struct ls2k_bmc_ddata *ddata = arg; |
| 311 | + static unsigned long last_jiffies; |
| 312 | + |
| 313 | + if (system_state != SYSTEM_RUNNING) |
| 314 | + return IRQ_HANDLED; |
| 315 | + |
| 316 | + /* Skip interrupt in LS2K_BMC_INT_INTERVAL */ |
| 317 | + if (time_after(jiffies, last_jiffies + LS2K_BMC_INT_INTERVAL)) { |
| 318 | + schedule_work(&ddata->bmc_reset_work); |
| 319 | + last_jiffies = jiffies; |
| 320 | + } |
| 321 | + |
| 322 | + return IRQ_HANDLED; |
| 323 | +} |
| 324 | + |
| 325 | +/* |
| 326 | + * Saves the BMC parent device (LS7A) and its own PCI configuration space registers |
| 327 | + * that need to be restored after BMC reset. |
| 328 | + */ |
| 329 | +static void ls2k_bmc_save_pci_data(struct pci_dev *pdev, struct ls2k_bmc_ddata *ddata) |
| 330 | +{ |
| 331 | + struct pci_dev *parent = pdev->bus->self; |
| 332 | + int base, i = 0; |
| 333 | + |
| 334 | + pci_read_config_dword(parent, PCI_COMMAND, &ddata->bridge_pci_data.pci_command); |
| 335 | + |
| 336 | + for (base = PCI_BASE_ADDRESS_0; base <= PCI_BASE_ADDRESS_5; base += PCI_REG_STRIDE, i++) |
| 337 | + pci_read_config_dword(parent, base, &ddata->bridge_pci_data.base_address[i]); |
| 338 | + |
| 339 | + pci_read_config_dword(parent, PCI_ROM_ADDRESS, &ddata->bridge_pci_data.rom_addreess); |
| 340 | + pci_read_config_dword(parent, PCI_INTERRUPT_LINE, &ddata->bridge_pci_data.interrupt_line); |
| 341 | + |
| 342 | + pci_read_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_LO, |
| 343 | + &ddata->bridge_pci_data.msi_lo); |
| 344 | + pci_read_config_dword(parent, parent->msi_cap + PCI_MSI_ADDRESS_HI, |
| 345 | + &ddata->bridge_pci_data.msi_hi); |
| 346 | + |
| 347 | + pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_DEVCTL, |
| 348 | + &ddata->bridge_pci_data.devctl); |
| 349 | + pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCAP, |
| 350 | + &ddata->bridge_pci_data.linkcap); |
| 351 | + pci_read_config_dword(parent, parent->pcie_cap + PCI_EXP_LNKCTL, |
| 352 | + &ddata->bridge_pci_data.linkctl_sts); |
| 353 | + |
| 354 | + pci_read_config_dword(parent, LS7A_GEN2_CTL, &ddata->bridge_pci_data.gen2_ctrl); |
| 355 | + ddata->bridge_pci_data.gen2_ctrl |= FIELD_PREP(LS7A_GEN2_SPEED_CHANG, 0x1) | |
| 356 | + FIELD_PREP(LS7A_CONF_PHY_TX, 0x0); |
| 357 | + |
| 358 | + pci_read_config_dword(parent, LS7A_SYMBOL_TIMER, &ddata->bridge_pci_data.symbol_timer); |
| 359 | + ddata->bridge_pci_data.symbol_timer |= LS7A_MASK_LEN_MATCH; |
| 360 | + |
| 361 | + pci_read_config_dword(pdev, PCI_COMMAND, &ddata->bmc_pci_data.pci_command); |
| 362 | + pci_read_config_dword(pdev, PCI_BASE_ADDRESS_0, &ddata->bmc_pci_data.base_address0); |
| 363 | + pci_read_config_dword(pdev, PCI_INTERRUPT_LINE, &ddata->bmc_pci_data.interrupt_line); |
| 364 | +} |
| 365 | + |
| 366 | +static int ls2k_bmc_init(struct ls2k_bmc_ddata *ddata) |
| 367 | +{ |
| 368 | + struct pci_dev *pdev = to_pci_dev(ddata->dev); |
| 369 | + void __iomem *gpio_base; |
| 370 | + int gpio_irq, ret, val; |
| 371 | + |
| 372 | + ls2k_bmc_save_pci_data(pdev, ddata); |
| 373 | + |
| 374 | + INIT_WORK(&ddata->bmc_reset_work, ls2k_bmc_events_fn); |
| 375 | + |
| 376 | + ret = devm_request_irq(&pdev->dev, pdev->irq, ls2k_bmc_interrupt, |
| 377 | + IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc pcie", ddata); |
| 378 | + if (ret) { |
| 379 | + dev_err(ddata->dev, "Failed to request LS2KBMC PCI-E IRQ %d.\n", pdev->irq); |
| 380 | + return ret; |
| 381 | + } |
| 382 | + |
| 383 | + gpio_base = ioremap(LOONGSON_GPIO_REG_BASE, LOONGSON_GPIO_REG_SIZE); |
| 384 | + if (!gpio_base) |
| 385 | + return -ENOMEM; |
| 386 | + |
| 387 | + /* Disable GPIO output */ |
| 388 | + val = readl(gpio_base + LOONGSON_GPIO_OEN); |
| 389 | + writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_OEN); |
| 390 | + |
| 391 | + /* Enable GPIO functionality */ |
| 392 | + val = readl(gpio_base + LOONGSON_GPIO_FUNC); |
| 393 | + writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_FUNC); |
| 394 | + |
| 395 | + /* Set GPIO interrupts to low-level active */ |
| 396 | + val = readl(gpio_base + LOONGSON_GPIO_INTPOL); |
| 397 | + writel(val & ~BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTPOL); |
| 398 | + |
| 399 | + /* Enable GPIO interrupts */ |
| 400 | + val = readl(gpio_base + LOONGSON_GPIO_INTEN); |
| 401 | + writel(val | BIT(LS2K_BMC_RESET_GPIO), gpio_base + LOONGSON_GPIO_INTEN); |
| 402 | + |
| 403 | + iounmap(gpio_base); |
| 404 | + |
| 405 | + /* |
| 406 | + * Since gpio_chip->to_irq is not implemented in the Loongson-3 GPIO driver, |
| 407 | + * acpi_register_gsi() is used to obtain the GPIO IRQ. The GPIO interrupt is a |
| 408 | + * watchdog interrupt that is triggered when the BMC resets. |
| 409 | + */ |
| 410 | + gpio_irq = acpi_register_gsi(NULL, LS2K_BMC_RESET_GPIO_GSI, ACPI_EDGE_SENSITIVE, |
| 411 | + ACPI_ACTIVE_LOW); |
| 412 | + if (gpio_irq < 0) |
| 413 | + return gpio_irq; |
| 414 | + |
| 415 | + ret = devm_request_irq(ddata->dev, gpio_irq, ls2k_bmc_interrupt, |
| 416 | + IRQF_SHARED | IRQF_TRIGGER_FALLING, "ls2kbmc gpio", ddata); |
| 417 | + if (ret) |
| 418 | + dev_err(ddata->dev, "Failed to request LS2KBMC GPIO IRQ %d.\n", gpio_irq); |
| 419 | + |
| 420 | + acpi_unregister_gsi(LS2K_BMC_RESET_GPIO_GSI); |
| 421 | + return ret; |
| 422 | +} |
| 423 | + |
98 | 424 | /* |
99 | 425 | * Currently the Loongson-2K BMC hardware does not have an I2C interface to adapt to the |
100 | 426 | * resolution. We set the resolution by presetting "video=1280x1024-16@2M" to the BMC memory. |
@@ -134,13 +460,26 @@ static int ls2k_bmc_parse_mode(struct pci_dev *pdev, struct simplefb_platform_da |
134 | 460 | static int ls2k_bmc_probe(struct pci_dev *dev, const struct pci_device_id *id) |
135 | 461 | { |
136 | 462 | struct simplefb_platform_data pd; |
| 463 | + struct ls2k_bmc_ddata *ddata; |
137 | 464 | resource_size_t base; |
138 | 465 | int ret; |
139 | 466 |
|
140 | 467 | ret = pci_enable_device(dev); |
141 | 468 | if (ret) |
142 | 469 | return ret; |
143 | 470 |
|
| 471 | + ddata = devm_kzalloc(&dev->dev, sizeof(*ddata), GFP_KERNEL); |
| 472 | + if (IS_ERR(ddata)) { |
| 473 | + ret = -ENOMEM; |
| 474 | + goto disable_pci; |
| 475 | + } |
| 476 | + |
| 477 | + ddata->dev = &dev->dev; |
| 478 | + |
| 479 | + ret = ls2k_bmc_init(ddata); |
| 480 | + if (ret) |
| 481 | + goto disable_pci; |
| 482 | + |
144 | 483 | ret = ls2k_bmc_parse_mode(dev, &pd); |
145 | 484 | if (ret) |
146 | 485 | goto disable_pci; |
|
0 commit comments