| .. | .. |
|---|
| 10 | 10 | #include <linux/interrupt.h> |
|---|
| 11 | 11 | #include <linux/irqchip/chained_irq.h> |
|---|
| 12 | 12 | #include <linux/init.h> |
|---|
| 13 | +#include <linux/module.h> |
|---|
| 13 | 14 | #include <linux/of_address.h> |
|---|
| 15 | +#include <linux/of_device.h> |
|---|
| 14 | 16 | #include <linux/of_irq.h> |
|---|
| 15 | 17 | #include <linux/of_pci.h> |
|---|
| 16 | 18 | #include <linux/pci.h> |
|---|
| .. | .. |
|---|
| 37 | 39 | #define RP_LTSSM_MASK 0x1f |
|---|
| 38 | 40 | #define LTSSM_L0 0xf |
|---|
| 39 | 41 | |
|---|
| 40 | | -#define PCIE_CAP_OFFSET 0x80 |
|---|
| 42 | +#define S10_RP_TX_CNTRL 0x2004 |
|---|
| 43 | +#define S10_RP_RXCPL_REG 0x2008 |
|---|
| 44 | +#define S10_RP_RXCPL_STATUS 0x200C |
|---|
| 45 | +#define S10_RP_CFG_ADDR(pcie, reg) \ |
|---|
| 46 | + (((pcie)->hip_base) + (reg) + (1 << 20)) |
|---|
| 47 | +#define S10_RP_SECONDARY(pcie) \ |
|---|
| 48 | + readb(S10_RP_CFG_ADDR(pcie, PCI_SECONDARY_BUS)) |
|---|
| 49 | + |
|---|
| 41 | 50 | /* TLP configuration type 0 and 1 */ |
|---|
| 42 | 51 | #define TLP_FMTTYPE_CFGRD0 0x04 /* Configuration Read Type 0 */ |
|---|
| 43 | 52 | #define TLP_FMTTYPE_CFGWR0 0x44 /* Configuration Write Type 0 */ |
|---|
| .. | .. |
|---|
| 48 | 57 | #define TLP_WRITE_TAG 0x10 |
|---|
| 49 | 58 | #define RP_DEVFN 0 |
|---|
| 50 | 59 | #define TLP_REQ_ID(bus, devfn) (((bus) << 8) | (devfn)) |
|---|
| 51 | | -#define TLP_CFGRD_DW0(pcie, bus) \ |
|---|
| 52 | | - ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGRD0 \ |
|---|
| 53 | | - : TLP_FMTTYPE_CFGRD1) << 24) | \ |
|---|
| 54 | | - TLP_PAYLOAD_SIZE) |
|---|
| 55 | | -#define TLP_CFGWR_DW0(pcie, bus) \ |
|---|
| 56 | | - ((((bus == pcie->root_bus_nr) ? TLP_FMTTYPE_CFGWR0 \ |
|---|
| 57 | | - : TLP_FMTTYPE_CFGWR1) << 24) | \ |
|---|
| 58 | | - TLP_PAYLOAD_SIZE) |
|---|
| 60 | +#define TLP_CFG_DW0(pcie, cfg) \ |
|---|
| 61 | + (((cfg) << 24) | \ |
|---|
| 62 | + TLP_PAYLOAD_SIZE) |
|---|
| 59 | 63 | #define TLP_CFG_DW1(pcie, tag, be) \ |
|---|
| 60 | | - (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) |
|---|
| 64 | + (((TLP_REQ_ID(pcie->root_bus_nr, RP_DEVFN)) << 16) | (tag << 8) | (be)) |
|---|
| 61 | 65 | #define TLP_CFG_DW2(bus, devfn, offset) \ |
|---|
| 62 | 66 | (((bus) << 24) | ((devfn) << 16) | (offset)) |
|---|
| 63 | 67 | #define TLP_COMP_STATUS(s) (((s) >> 13) & 7) |
|---|
| 68 | +#define TLP_BYTE_COUNT(s) (((s) >> 0) & 0xfff) |
|---|
| 64 | 69 | #define TLP_HDR_SIZE 3 |
|---|
| 65 | 70 | #define TLP_LOOP 500 |
|---|
| 66 | 71 | |
|---|
| .. | .. |
|---|
| 69 | 74 | |
|---|
| 70 | 75 | #define DWORD_MASK 3 |
|---|
| 71 | 76 | |
|---|
| 77 | +#define S10_TLP_FMTTYPE_CFGRD0 0x05 |
|---|
| 78 | +#define S10_TLP_FMTTYPE_CFGRD1 0x04 |
|---|
| 79 | +#define S10_TLP_FMTTYPE_CFGWR0 0x45 |
|---|
| 80 | +#define S10_TLP_FMTTYPE_CFGWR1 0x44 |
|---|
| 81 | + |
|---|
| 82 | +enum altera_pcie_version { |
|---|
| 83 | + ALTERA_PCIE_V1 = 0, |
|---|
| 84 | + ALTERA_PCIE_V2, |
|---|
| 85 | +}; |
|---|
| 86 | + |
|---|
| 72 | 87 | struct altera_pcie { |
|---|
| 73 | 88 | struct platform_device *pdev; |
|---|
| 74 | | - void __iomem *cra_base; /* DT Cra */ |
|---|
| 89 | + void __iomem *cra_base; |
|---|
| 90 | + void __iomem *hip_base; |
|---|
| 75 | 91 | int irq; |
|---|
| 76 | 92 | u8 root_bus_nr; |
|---|
| 77 | 93 | struct irq_domain *irq_domain; |
|---|
| 78 | 94 | struct resource bus_range; |
|---|
| 79 | | - struct list_head resources; |
|---|
| 95 | + const struct altera_pcie_data *pcie_data; |
|---|
| 96 | +}; |
|---|
| 97 | + |
|---|
| 98 | +struct altera_pcie_ops { |
|---|
| 99 | + int (*tlp_read_pkt)(struct altera_pcie *pcie, u32 *value); |
|---|
| 100 | + void (*tlp_write_pkt)(struct altera_pcie *pcie, u32 *headers, |
|---|
| 101 | + u32 data, bool align); |
|---|
| 102 | + bool (*get_link_status)(struct altera_pcie *pcie); |
|---|
| 103 | + int (*rp_read_cfg)(struct altera_pcie *pcie, int where, |
|---|
| 104 | + int size, u32 *value); |
|---|
| 105 | + int (*rp_write_cfg)(struct altera_pcie *pcie, u8 busno, |
|---|
| 106 | + int where, int size, u32 value); |
|---|
| 107 | +}; |
|---|
| 108 | + |
|---|
| 109 | +struct altera_pcie_data { |
|---|
| 110 | + const struct altera_pcie_ops *ops; |
|---|
| 111 | + enum altera_pcie_version version; |
|---|
| 112 | + u32 cap_offset; /* PCIe capability structure register offset */ |
|---|
| 113 | + u32 cfgrd0; |
|---|
| 114 | + u32 cfgrd1; |
|---|
| 115 | + u32 cfgwr0; |
|---|
| 116 | + u32 cfgwr1; |
|---|
| 80 | 117 | }; |
|---|
| 81 | 118 | |
|---|
| 82 | 119 | struct tlp_rp_regpair_t { |
|---|
| .. | .. |
|---|
| 99 | 136 | static bool altera_pcie_link_up(struct altera_pcie *pcie) |
|---|
| 100 | 137 | { |
|---|
| 101 | 138 | return !!((cra_readl(pcie, RP_LTSSM) & RP_LTSSM_MASK) == LTSSM_L0); |
|---|
| 139 | +} |
|---|
| 140 | + |
|---|
| 141 | +static bool s10_altera_pcie_link_up(struct altera_pcie *pcie) |
|---|
| 142 | +{ |
|---|
| 143 | + void __iomem *addr = S10_RP_CFG_ADDR(pcie, |
|---|
| 144 | + pcie->pcie_data->cap_offset + |
|---|
| 145 | + PCI_EXP_LNKSTA); |
|---|
| 146 | + |
|---|
| 147 | + return !!(readw(addr) & PCI_EXP_LNKSTA_DLLLA); |
|---|
| 102 | 148 | } |
|---|
| 103 | 149 | |
|---|
| 104 | 150 | /* |
|---|
| .. | .. |
|---|
| 128 | 174 | cra_writel(pcie, tlp_rp_regdata->ctrl, RP_TX_CNTRL); |
|---|
| 129 | 175 | } |
|---|
| 130 | 176 | |
|---|
| 177 | +static void s10_tlp_write_tx(struct altera_pcie *pcie, u32 reg0, u32 ctrl) |
|---|
| 178 | +{ |
|---|
| 179 | + cra_writel(pcie, reg0, RP_TX_REG0); |
|---|
| 180 | + cra_writel(pcie, ctrl, S10_RP_TX_CNTRL); |
|---|
| 181 | +} |
|---|
| 182 | + |
|---|
| 131 | 183 | static bool altera_pcie_valid_device(struct altera_pcie *pcie, |
|---|
| 132 | 184 | struct pci_bus *bus, int dev) |
|---|
| 133 | 185 | { |
|---|
| 134 | 186 | /* If there is no link, then there is no device */ |
|---|
| 135 | 187 | if (bus->number != pcie->root_bus_nr) { |
|---|
| 136 | | - if (!altera_pcie_link_up(pcie)) |
|---|
| 188 | + if (!pcie->pcie_data->ops->get_link_status(pcie)) |
|---|
| 137 | 189 | return false; |
|---|
| 138 | 190 | } |
|---|
| 139 | 191 | |
|---|
| .. | .. |
|---|
| 141 | 193 | if (bus->number == pcie->root_bus_nr && dev > 0) |
|---|
| 142 | 194 | return false; |
|---|
| 143 | 195 | |
|---|
| 144 | | - return true; |
|---|
| 196 | + return true; |
|---|
| 145 | 197 | } |
|---|
| 146 | 198 | |
|---|
| 147 | 199 | static int tlp_read_packet(struct altera_pcie *pcie, u32 *value) |
|---|
| .. | .. |
|---|
| 183 | 235 | return PCIBIOS_DEVICE_NOT_FOUND; |
|---|
| 184 | 236 | } |
|---|
| 185 | 237 | |
|---|
| 238 | +static int s10_tlp_read_packet(struct altera_pcie *pcie, u32 *value) |
|---|
| 239 | +{ |
|---|
| 240 | + u32 ctrl; |
|---|
| 241 | + u32 comp_status; |
|---|
| 242 | + u32 dw[4]; |
|---|
| 243 | + u32 count; |
|---|
| 244 | + struct device *dev = &pcie->pdev->dev; |
|---|
| 245 | + |
|---|
| 246 | + for (count = 0; count < TLP_LOOP; count++) { |
|---|
| 247 | + ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS); |
|---|
| 248 | + if (ctrl & RP_RXCPL_SOP) { |
|---|
| 249 | + /* Read first DW */ |
|---|
| 250 | + dw[0] = cra_readl(pcie, S10_RP_RXCPL_REG); |
|---|
| 251 | + break; |
|---|
| 252 | + } |
|---|
| 253 | + |
|---|
| 254 | + udelay(5); |
|---|
| 255 | + } |
|---|
| 256 | + |
|---|
| 257 | + /* SOP detection failed, return error */ |
|---|
| 258 | + if (count == TLP_LOOP) |
|---|
| 259 | + return PCIBIOS_DEVICE_NOT_FOUND; |
|---|
| 260 | + |
|---|
| 261 | + count = 1; |
|---|
| 262 | + |
|---|
| 263 | + /* Poll for EOP */ |
|---|
| 264 | + while (count < ARRAY_SIZE(dw)) { |
|---|
| 265 | + ctrl = cra_readl(pcie, S10_RP_RXCPL_STATUS); |
|---|
| 266 | + dw[count++] = cra_readl(pcie, S10_RP_RXCPL_REG); |
|---|
| 267 | + if (ctrl & RP_RXCPL_EOP) { |
|---|
| 268 | + comp_status = TLP_COMP_STATUS(dw[1]); |
|---|
| 269 | + if (comp_status) |
|---|
| 270 | + return PCIBIOS_DEVICE_NOT_FOUND; |
|---|
| 271 | + |
|---|
| 272 | + if (value && TLP_BYTE_COUNT(dw[1]) == sizeof(u32) && |
|---|
| 273 | + count == 4) |
|---|
| 274 | + *value = dw[3]; |
|---|
| 275 | + |
|---|
| 276 | + return PCIBIOS_SUCCESSFUL; |
|---|
| 277 | + } |
|---|
| 278 | + } |
|---|
| 279 | + |
|---|
| 280 | + dev_warn(dev, "Malformed TLP packet\n"); |
|---|
| 281 | + |
|---|
| 282 | + return PCIBIOS_DEVICE_NOT_FOUND; |
|---|
| 283 | +} |
|---|
| 284 | + |
|---|
| 186 | 285 | static void tlp_write_packet(struct altera_pcie *pcie, u32 *headers, |
|---|
| 187 | 286 | u32 data, bool align) |
|---|
| 188 | 287 | { |
|---|
| .. | .. |
|---|
| 210 | 309 | tlp_write_tx(pcie, &tlp_rp_regdata); |
|---|
| 211 | 310 | } |
|---|
| 212 | 311 | |
|---|
| 312 | +static void s10_tlp_write_packet(struct altera_pcie *pcie, u32 *headers, |
|---|
| 313 | + u32 data, bool dummy) |
|---|
| 314 | +{ |
|---|
| 315 | + s10_tlp_write_tx(pcie, headers[0], RP_TX_SOP); |
|---|
| 316 | + s10_tlp_write_tx(pcie, headers[1], 0); |
|---|
| 317 | + s10_tlp_write_tx(pcie, headers[2], 0); |
|---|
| 318 | + s10_tlp_write_tx(pcie, data, RP_TX_EOP); |
|---|
| 319 | +} |
|---|
| 320 | + |
|---|
| 321 | +static void get_tlp_header(struct altera_pcie *pcie, u8 bus, u32 devfn, |
|---|
| 322 | + int where, u8 byte_en, bool read, u32 *headers) |
|---|
| 323 | +{ |
|---|
| 324 | + u8 cfg; |
|---|
| 325 | + u8 cfg0 = read ? pcie->pcie_data->cfgrd0 : pcie->pcie_data->cfgwr0; |
|---|
| 326 | + u8 cfg1 = read ? pcie->pcie_data->cfgrd1 : pcie->pcie_data->cfgwr1; |
|---|
| 327 | + u8 tag = read ? TLP_READ_TAG : TLP_WRITE_TAG; |
|---|
| 328 | + |
|---|
| 329 | + if (pcie->pcie_data->version == ALTERA_PCIE_V1) |
|---|
| 330 | + cfg = (bus == pcie->root_bus_nr) ? cfg0 : cfg1; |
|---|
| 331 | + else |
|---|
| 332 | + cfg = (bus > S10_RP_SECONDARY(pcie)) ? cfg0 : cfg1; |
|---|
| 333 | + |
|---|
| 334 | + headers[0] = TLP_CFG_DW0(pcie, cfg); |
|---|
| 335 | + headers[1] = TLP_CFG_DW1(pcie, tag, byte_en); |
|---|
| 336 | + headers[2] = TLP_CFG_DW2(bus, devfn, where); |
|---|
| 337 | +} |
|---|
| 338 | + |
|---|
| 213 | 339 | static int tlp_cfg_dword_read(struct altera_pcie *pcie, u8 bus, u32 devfn, |
|---|
| 214 | 340 | int where, u8 byte_en, u32 *value) |
|---|
| 215 | 341 | { |
|---|
| 216 | 342 | u32 headers[TLP_HDR_SIZE]; |
|---|
| 217 | 343 | |
|---|
| 218 | | - headers[0] = TLP_CFGRD_DW0(pcie, bus); |
|---|
| 219 | | - headers[1] = TLP_CFG_DW1(pcie, TLP_READ_TAG, byte_en); |
|---|
| 220 | | - headers[2] = TLP_CFG_DW2(bus, devfn, where); |
|---|
| 344 | + get_tlp_header(pcie, bus, devfn, where, byte_en, true, |
|---|
| 345 | + headers); |
|---|
| 221 | 346 | |
|---|
| 222 | | - tlp_write_packet(pcie, headers, 0, false); |
|---|
| 347 | + pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, 0, false); |
|---|
| 223 | 348 | |
|---|
| 224 | | - return tlp_read_packet(pcie, value); |
|---|
| 349 | + return pcie->pcie_data->ops->tlp_read_pkt(pcie, value); |
|---|
| 225 | 350 | } |
|---|
| 226 | 351 | |
|---|
| 227 | 352 | static int tlp_cfg_dword_write(struct altera_pcie *pcie, u8 bus, u32 devfn, |
|---|
| .. | .. |
|---|
| 230 | 355 | u32 headers[TLP_HDR_SIZE]; |
|---|
| 231 | 356 | int ret; |
|---|
| 232 | 357 | |
|---|
| 233 | | - headers[0] = TLP_CFGWR_DW0(pcie, bus); |
|---|
| 234 | | - headers[1] = TLP_CFG_DW1(pcie, TLP_WRITE_TAG, byte_en); |
|---|
| 235 | | - headers[2] = TLP_CFG_DW2(bus, devfn, where); |
|---|
| 358 | + get_tlp_header(pcie, bus, devfn, where, byte_en, false, |
|---|
| 359 | + headers); |
|---|
| 236 | 360 | |
|---|
| 237 | 361 | /* check alignment to Qword */ |
|---|
| 238 | 362 | if ((where & 0x7) == 0) |
|---|
| 239 | | - tlp_write_packet(pcie, headers, value, true); |
|---|
| 363 | + pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, |
|---|
| 364 | + value, true); |
|---|
| 240 | 365 | else |
|---|
| 241 | | - tlp_write_packet(pcie, headers, value, false); |
|---|
| 366 | + pcie->pcie_data->ops->tlp_write_pkt(pcie, headers, |
|---|
| 367 | + value, false); |
|---|
| 242 | 368 | |
|---|
| 243 | | - ret = tlp_read_packet(pcie, NULL); |
|---|
| 369 | + ret = pcie->pcie_data->ops->tlp_read_pkt(pcie, NULL); |
|---|
| 244 | 370 | if (ret != PCIBIOS_SUCCESSFUL) |
|---|
| 245 | 371 | return ret; |
|---|
| 246 | 372 | |
|---|
| .. | .. |
|---|
| 254 | 380 | return PCIBIOS_SUCCESSFUL; |
|---|
| 255 | 381 | } |
|---|
| 256 | 382 | |
|---|
| 383 | +static int s10_rp_read_cfg(struct altera_pcie *pcie, int where, |
|---|
| 384 | + int size, u32 *value) |
|---|
| 385 | +{ |
|---|
| 386 | + void __iomem *addr = S10_RP_CFG_ADDR(pcie, where); |
|---|
| 387 | + |
|---|
| 388 | + switch (size) { |
|---|
| 389 | + case 1: |
|---|
| 390 | + *value = readb(addr); |
|---|
| 391 | + break; |
|---|
| 392 | + case 2: |
|---|
| 393 | + *value = readw(addr); |
|---|
| 394 | + break; |
|---|
| 395 | + default: |
|---|
| 396 | + *value = readl(addr); |
|---|
| 397 | + break; |
|---|
| 398 | + } |
|---|
| 399 | + |
|---|
| 400 | + return PCIBIOS_SUCCESSFUL; |
|---|
| 401 | +} |
|---|
| 402 | + |
|---|
| 403 | +static int s10_rp_write_cfg(struct altera_pcie *pcie, u8 busno, |
|---|
| 404 | + int where, int size, u32 value) |
|---|
| 405 | +{ |
|---|
| 406 | + void __iomem *addr = S10_RP_CFG_ADDR(pcie, where); |
|---|
| 407 | + |
|---|
| 408 | + switch (size) { |
|---|
| 409 | + case 1: |
|---|
| 410 | + writeb(value, addr); |
|---|
| 411 | + break; |
|---|
| 412 | + case 2: |
|---|
| 413 | + writew(value, addr); |
|---|
| 414 | + break; |
|---|
| 415 | + default: |
|---|
| 416 | + writel(value, addr); |
|---|
| 417 | + break; |
|---|
| 418 | + } |
|---|
| 419 | + |
|---|
| 420 | + /* |
|---|
| 421 | + * Monitor changes to PCI_PRIMARY_BUS register on root port |
|---|
| 422 | + * and update local copy of root bus number accordingly. |
|---|
| 423 | + */ |
|---|
| 424 | + if (busno == pcie->root_bus_nr && where == PCI_PRIMARY_BUS) |
|---|
| 425 | + pcie->root_bus_nr = value & 0xff; |
|---|
| 426 | + |
|---|
| 427 | + return PCIBIOS_SUCCESSFUL; |
|---|
| 428 | +} |
|---|
| 429 | + |
|---|
| 257 | 430 | static int _altera_pcie_cfg_read(struct altera_pcie *pcie, u8 busno, |
|---|
| 258 | 431 | unsigned int devfn, int where, int size, |
|---|
| 259 | 432 | u32 *value) |
|---|
| .. | .. |
|---|
| 261 | 434 | int ret; |
|---|
| 262 | 435 | u32 data; |
|---|
| 263 | 436 | u8 byte_en; |
|---|
| 437 | + |
|---|
| 438 | + if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_read_cfg) |
|---|
| 439 | + return pcie->pcie_data->ops->rp_read_cfg(pcie, where, |
|---|
| 440 | + size, value); |
|---|
| 264 | 441 | |
|---|
| 265 | 442 | switch (size) { |
|---|
| 266 | 443 | case 1: |
|---|
| .. | .. |
|---|
| 301 | 478 | u32 data32; |
|---|
| 302 | 479 | u32 shift = 8 * (where & 3); |
|---|
| 303 | 480 | u8 byte_en; |
|---|
| 481 | + |
|---|
| 482 | + if (busno == pcie->root_bus_nr && pcie->pcie_data->ops->rp_write_cfg) |
|---|
| 483 | + return pcie->pcie_data->ops->rp_write_cfg(pcie, busno, |
|---|
| 484 | + where, size, value); |
|---|
| 304 | 485 | |
|---|
| 305 | 486 | switch (size) { |
|---|
| 306 | 487 | case 1: |
|---|
| .. | .. |
|---|
| 365 | 546 | int ret; |
|---|
| 366 | 547 | |
|---|
| 367 | 548 | ret = _altera_pcie_cfg_read(pcie, busno, devfn, |
|---|
| 368 | | - PCIE_CAP_OFFSET + offset, sizeof(*value), |
|---|
| 549 | + pcie->pcie_data->cap_offset + offset, |
|---|
| 550 | + sizeof(*value), |
|---|
| 369 | 551 | &data); |
|---|
| 370 | 552 | *value = data; |
|---|
| 371 | 553 | return ret; |
|---|
| .. | .. |
|---|
| 375 | 557 | unsigned int devfn, int offset, u16 value) |
|---|
| 376 | 558 | { |
|---|
| 377 | 559 | return _altera_pcie_cfg_write(pcie, busno, devfn, |
|---|
| 378 | | - PCIE_CAP_OFFSET + offset, sizeof(value), |
|---|
| 560 | + pcie->pcie_data->cap_offset + offset, |
|---|
| 561 | + sizeof(value), |
|---|
| 379 | 562 | value); |
|---|
| 380 | 563 | } |
|---|
| 381 | 564 | |
|---|
| .. | .. |
|---|
| 403 | 586 | /* Wait for link is up */ |
|---|
| 404 | 587 | start_jiffies = jiffies; |
|---|
| 405 | 588 | for (;;) { |
|---|
| 406 | | - if (altera_pcie_link_up(pcie)) |
|---|
| 589 | + if (pcie->pcie_data->ops->get_link_status(pcie)) |
|---|
| 407 | 590 | break; |
|---|
| 408 | 591 | |
|---|
| 409 | 592 | if (time_after(jiffies, start_jiffies + LINK_UP_TIMEOUT)) { |
|---|
| .. | .. |
|---|
| 418 | 601 | { |
|---|
| 419 | 602 | u16 linkcap, linkstat, linkctl; |
|---|
| 420 | 603 | |
|---|
| 421 | | - if (!altera_pcie_link_up(pcie)) |
|---|
| 604 | + if (!pcie->pcie_data->ops->get_link_status(pcie)) |
|---|
| 422 | 605 | return; |
|---|
| 423 | 606 | |
|---|
| 424 | 607 | /* |
|---|
| .. | .. |
|---|
| 486 | 669 | chained_irq_exit(chip, desc); |
|---|
| 487 | 670 | } |
|---|
| 488 | 671 | |
|---|
| 489 | | -static int altera_pcie_parse_request_of_pci_ranges(struct altera_pcie *pcie) |
|---|
| 490 | | -{ |
|---|
| 491 | | - int err, res_valid = 0; |
|---|
| 492 | | - struct device *dev = &pcie->pdev->dev; |
|---|
| 493 | | - struct resource_entry *win; |
|---|
| 494 | | - |
|---|
| 495 | | - err = devm_of_pci_get_host_bridge_resources(dev, 0, 0xff, |
|---|
| 496 | | - &pcie->resources, NULL); |
|---|
| 497 | | - if (err) |
|---|
| 498 | | - return err; |
|---|
| 499 | | - |
|---|
| 500 | | - err = devm_request_pci_bus_resources(dev, &pcie->resources); |
|---|
| 501 | | - if (err) |
|---|
| 502 | | - goto out_release_res; |
|---|
| 503 | | - |
|---|
| 504 | | - resource_list_for_each_entry(win, &pcie->resources) { |
|---|
| 505 | | - struct resource *res = win->res; |
|---|
| 506 | | - |
|---|
| 507 | | - if (resource_type(res) == IORESOURCE_MEM) |
|---|
| 508 | | - res_valid |= !(res->flags & IORESOURCE_PREFETCH); |
|---|
| 509 | | - } |
|---|
| 510 | | - |
|---|
| 511 | | - if (res_valid) |
|---|
| 512 | | - return 0; |
|---|
| 513 | | - |
|---|
| 514 | | - dev_err(dev, "non-prefetchable memory resource required\n"); |
|---|
| 515 | | - err = -EINVAL; |
|---|
| 516 | | - |
|---|
| 517 | | -out_release_res: |
|---|
| 518 | | - pci_free_resource_list(&pcie->resources); |
|---|
| 519 | | - return err; |
|---|
| 520 | | -} |
|---|
| 521 | | - |
|---|
| 522 | 672 | static int altera_pcie_init_irq_domain(struct altera_pcie *pcie) |
|---|
| 523 | 673 | { |
|---|
| 524 | 674 | struct device *dev = &pcie->pdev->dev; |
|---|
| .. | .. |
|---|
| 535 | 685 | return 0; |
|---|
| 536 | 686 | } |
|---|
| 537 | 687 | |
|---|
| 688 | +static void altera_pcie_irq_teardown(struct altera_pcie *pcie) |
|---|
| 689 | +{ |
|---|
| 690 | + irq_set_chained_handler_and_data(pcie->irq, NULL, NULL); |
|---|
| 691 | + irq_domain_remove(pcie->irq_domain); |
|---|
| 692 | + irq_dispose_mapping(pcie->irq); |
|---|
| 693 | +} |
|---|
| 694 | + |
|---|
| 538 | 695 | static int altera_pcie_parse_dt(struct altera_pcie *pcie) |
|---|
| 539 | 696 | { |
|---|
| 540 | | - struct device *dev = &pcie->pdev->dev; |
|---|
| 541 | 697 | struct platform_device *pdev = pcie->pdev; |
|---|
| 542 | | - struct resource *cra; |
|---|
| 543 | 698 | |
|---|
| 544 | | - cra = platform_get_resource_byname(pdev, IORESOURCE_MEM, "Cra"); |
|---|
| 545 | | - pcie->cra_base = devm_ioremap_resource(dev, cra); |
|---|
| 699 | + pcie->cra_base = devm_platform_ioremap_resource_byname(pdev, "Cra"); |
|---|
| 546 | 700 | if (IS_ERR(pcie->cra_base)) |
|---|
| 547 | 701 | return PTR_ERR(pcie->cra_base); |
|---|
| 548 | 702 | |
|---|
| 703 | + if (pcie->pcie_data->version == ALTERA_PCIE_V2) { |
|---|
| 704 | + pcie->hip_base = |
|---|
| 705 | + devm_platform_ioremap_resource_byname(pdev, "Hip"); |
|---|
| 706 | + if (IS_ERR(pcie->hip_base)) |
|---|
| 707 | + return PTR_ERR(pcie->hip_base); |
|---|
| 708 | + } |
|---|
| 709 | + |
|---|
| 549 | 710 | /* setup IRQ */ |
|---|
| 550 | 711 | pcie->irq = platform_get_irq(pdev, 0); |
|---|
| 551 | | - if (pcie->irq < 0) { |
|---|
| 552 | | - dev_err(dev, "failed to get IRQ: %d\n", pcie->irq); |
|---|
| 712 | + if (pcie->irq < 0) |
|---|
| 553 | 713 | return pcie->irq; |
|---|
| 554 | | - } |
|---|
| 555 | 714 | |
|---|
| 556 | 715 | irq_set_chained_handler_and_data(pcie->irq, altera_pcie_isr, pcie); |
|---|
| 557 | 716 | return 0; |
|---|
| .. | .. |
|---|
| 562 | 721 | altera_pcie_retrain(pcie); |
|---|
| 563 | 722 | } |
|---|
| 564 | 723 | |
|---|
| 724 | +static const struct altera_pcie_ops altera_pcie_ops_1_0 = { |
|---|
| 725 | + .tlp_read_pkt = tlp_read_packet, |
|---|
| 726 | + .tlp_write_pkt = tlp_write_packet, |
|---|
| 727 | + .get_link_status = altera_pcie_link_up, |
|---|
| 728 | +}; |
|---|
| 729 | + |
|---|
| 730 | +static const struct altera_pcie_ops altera_pcie_ops_2_0 = { |
|---|
| 731 | + .tlp_read_pkt = s10_tlp_read_packet, |
|---|
| 732 | + .tlp_write_pkt = s10_tlp_write_packet, |
|---|
| 733 | + .get_link_status = s10_altera_pcie_link_up, |
|---|
| 734 | + .rp_read_cfg = s10_rp_read_cfg, |
|---|
| 735 | + .rp_write_cfg = s10_rp_write_cfg, |
|---|
| 736 | +}; |
|---|
| 737 | + |
|---|
| 738 | +static const struct altera_pcie_data altera_pcie_1_0_data = { |
|---|
| 739 | + .ops = &altera_pcie_ops_1_0, |
|---|
| 740 | + .cap_offset = 0x80, |
|---|
| 741 | + .version = ALTERA_PCIE_V1, |
|---|
| 742 | + .cfgrd0 = TLP_FMTTYPE_CFGRD0, |
|---|
| 743 | + .cfgrd1 = TLP_FMTTYPE_CFGRD1, |
|---|
| 744 | + .cfgwr0 = TLP_FMTTYPE_CFGWR0, |
|---|
| 745 | + .cfgwr1 = TLP_FMTTYPE_CFGWR1, |
|---|
| 746 | +}; |
|---|
| 747 | + |
|---|
| 748 | +static const struct altera_pcie_data altera_pcie_2_0_data = { |
|---|
| 749 | + .ops = &altera_pcie_ops_2_0, |
|---|
| 750 | + .version = ALTERA_PCIE_V2, |
|---|
| 751 | + .cap_offset = 0x70, |
|---|
| 752 | + .cfgrd0 = S10_TLP_FMTTYPE_CFGRD0, |
|---|
| 753 | + .cfgrd1 = S10_TLP_FMTTYPE_CFGRD1, |
|---|
| 754 | + .cfgwr0 = S10_TLP_FMTTYPE_CFGWR0, |
|---|
| 755 | + .cfgwr1 = S10_TLP_FMTTYPE_CFGWR1, |
|---|
| 756 | +}; |
|---|
| 757 | + |
|---|
| 758 | +static const struct of_device_id altera_pcie_of_match[] = { |
|---|
| 759 | + {.compatible = "altr,pcie-root-port-1.0", |
|---|
| 760 | + .data = &altera_pcie_1_0_data }, |
|---|
| 761 | + {.compatible = "altr,pcie-root-port-2.0", |
|---|
| 762 | + .data = &altera_pcie_2_0_data }, |
|---|
| 763 | + {}, |
|---|
| 764 | +}; |
|---|
| 765 | + |
|---|
| 565 | 766 | static int altera_pcie_probe(struct platform_device *pdev) |
|---|
| 566 | 767 | { |
|---|
| 567 | 768 | struct device *dev = &pdev->dev; |
|---|
| 568 | 769 | struct altera_pcie *pcie; |
|---|
| 569 | | - struct pci_bus *bus; |
|---|
| 570 | | - struct pci_bus *child; |
|---|
| 571 | 770 | struct pci_host_bridge *bridge; |
|---|
| 572 | 771 | int ret; |
|---|
| 772 | + const struct of_device_id *match; |
|---|
| 573 | 773 | |
|---|
| 574 | 774 | bridge = devm_pci_alloc_host_bridge(dev, sizeof(*pcie)); |
|---|
| 575 | 775 | if (!bridge) |
|---|
| .. | .. |
|---|
| 577 | 777 | |
|---|
| 578 | 778 | pcie = pci_host_bridge_priv(bridge); |
|---|
| 579 | 779 | pcie->pdev = pdev; |
|---|
| 780 | + platform_set_drvdata(pdev, pcie); |
|---|
| 781 | + |
|---|
| 782 | + match = of_match_device(altera_pcie_of_match, &pdev->dev); |
|---|
| 783 | + if (!match) |
|---|
| 784 | + return -ENODEV; |
|---|
| 785 | + |
|---|
| 786 | + pcie->pcie_data = match->data; |
|---|
| 580 | 787 | |
|---|
| 581 | 788 | ret = altera_pcie_parse_dt(pcie); |
|---|
| 582 | 789 | if (ret) { |
|---|
| 583 | 790 | dev_err(dev, "Parsing DT failed\n"); |
|---|
| 584 | | - return ret; |
|---|
| 585 | | - } |
|---|
| 586 | | - |
|---|
| 587 | | - INIT_LIST_HEAD(&pcie->resources); |
|---|
| 588 | | - |
|---|
| 589 | | - ret = altera_pcie_parse_request_of_pci_ranges(pcie); |
|---|
| 590 | | - if (ret) { |
|---|
| 591 | | - dev_err(dev, "Failed add resources\n"); |
|---|
| 592 | 791 | return ret; |
|---|
| 593 | 792 | } |
|---|
| 594 | 793 | |
|---|
| .. | .. |
|---|
| 604 | 803 | cra_writel(pcie, P2A_INT_ENA_ALL, P2A_INT_ENABLE); |
|---|
| 605 | 804 | altera_pcie_host_init(pcie); |
|---|
| 606 | 805 | |
|---|
| 607 | | - list_splice_init(&pcie->resources, &bridge->windows); |
|---|
| 608 | | - bridge->dev.parent = dev; |
|---|
| 609 | 806 | bridge->sysdata = pcie; |
|---|
| 610 | 807 | bridge->busnr = pcie->root_bus_nr; |
|---|
| 611 | 808 | bridge->ops = &altera_pcie_ops; |
|---|
| 612 | | - bridge->map_irq = of_irq_parse_and_map_pci; |
|---|
| 613 | | - bridge->swizzle_irq = pci_common_swizzle; |
|---|
| 614 | 809 | |
|---|
| 615 | | - ret = pci_scan_root_bus_bridge(bridge); |
|---|
| 616 | | - if (ret < 0) |
|---|
| 617 | | - return ret; |
|---|
| 618 | | - |
|---|
| 619 | | - bus = bridge->bus; |
|---|
| 620 | | - |
|---|
| 621 | | - pci_assign_unassigned_bus_resources(bus); |
|---|
| 622 | | - |
|---|
| 623 | | - /* Configure PCI Express setting. */ |
|---|
| 624 | | - list_for_each_entry(child, &bus->children, node) |
|---|
| 625 | | - pcie_bus_configure_settings(child); |
|---|
| 626 | | - |
|---|
| 627 | | - pci_bus_add_devices(bus); |
|---|
| 628 | | - return ret; |
|---|
| 810 | + return pci_host_probe(bridge); |
|---|
| 629 | 811 | } |
|---|
| 630 | 812 | |
|---|
| 631 | | -static const struct of_device_id altera_pcie_of_match[] = { |
|---|
| 632 | | - { .compatible = "altr,pcie-root-port-1.0", }, |
|---|
| 633 | | - {}, |
|---|
| 634 | | -}; |
|---|
| 813 | +static int altera_pcie_remove(struct platform_device *pdev) |
|---|
| 814 | +{ |
|---|
| 815 | + struct altera_pcie *pcie = platform_get_drvdata(pdev); |
|---|
| 816 | + struct pci_host_bridge *bridge = pci_host_bridge_from_priv(pcie); |
|---|
| 817 | + |
|---|
| 818 | + pci_stop_root_bus(bridge->bus); |
|---|
| 819 | + pci_remove_root_bus(bridge->bus); |
|---|
| 820 | + altera_pcie_irq_teardown(pcie); |
|---|
| 821 | + |
|---|
| 822 | + return 0; |
|---|
| 823 | +} |
|---|
| 635 | 824 | |
|---|
| 636 | 825 | static struct platform_driver altera_pcie_driver = { |
|---|
| 637 | 826 | .probe = altera_pcie_probe, |
|---|
| 827 | + .remove = altera_pcie_remove, |
|---|
| 638 | 828 | .driver = { |
|---|
| 639 | 829 | .name = "altera-pcie", |
|---|
| 640 | 830 | .of_match_table = altera_pcie_of_match, |
|---|
| 641 | | - .suppress_bind_attrs = true, |
|---|
| 642 | 831 | }, |
|---|
| 643 | 832 | }; |
|---|
| 644 | 833 | |
|---|
| 645 | | -builtin_platform_driver(altera_pcie_driver); |
|---|
| 834 | +MODULE_DEVICE_TABLE(of, altera_pcie_of_match); |
|---|
| 835 | +module_platform_driver(altera_pcie_driver); |
|---|
| 836 | +MODULE_LICENSE("GPL v2"); |
|---|