1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098 |
- From b2ee6e29bad31facbbf5ac1ce98235ac163d9fa9 Mon Sep 17 00:00:00 2001
- From: Yangbo Lu <[email protected]>
- Date: Thu, 5 Jul 2018 16:26:47 +0800
- Subject: [PATCH 08/32] pci: support layerscape
- This is an integrated patch for layerscape pcie support.
- Signed-off-by: Po Liu <[email protected]>
- Signed-off-by: Liu Gang <[email protected]>
- Signed-off-by: Minghuan Lian <[email protected]>
- Signed-off-by: hongbo.wang <[email protected]>
- Signed-off-by: Bjorn Helgaas <[email protected]>
- Signed-off-by: Hou Zhiqiang <[email protected]>
- Signed-off-by: Mingkai Hu <[email protected]>
- Signed-off-by: Christoph Hellwig <[email protected]>
- Signed-off-by: Yangbo Lu <[email protected]>
- ---
- drivers/irqchip/irq-ls-scfg-msi.c | 257 ++++++-
- drivers/pci/host/Makefile | 2 +-
- drivers/pci/host/pci-layerscape-ep-debugfs.c | 758 +++++++++++++++++++
- drivers/pci/host/pci-layerscape-ep.c | 309 ++++++++
- drivers/pci/host/pci-layerscape-ep.h | 115 +++
- drivers/pci/host/pci-layerscape.c | 48 +-
- drivers/pci/host/pcie-designware.c | 6 +
- drivers/pci/host/pcie-designware.h | 1 +
- drivers/pci/pci.c | 2 +-
- drivers/pci/pcie/portdrv_core.c | 181 ++---
- drivers/pci/quirks.c | 15 +
- include/linux/pci.h | 1 +
- 12 files changed, 1546 insertions(+), 149 deletions(-)
- create mode 100644 drivers/pci/host/pci-layerscape-ep-debugfs.c
- create mode 100644 drivers/pci/host/pci-layerscape-ep.c
- create mode 100644 drivers/pci/host/pci-layerscape-ep.h
- --- a/drivers/irqchip/irq-ls-scfg-msi.c
- +++ b/drivers/irqchip/irq-ls-scfg-msi.c
- @@ -17,13 +17,32 @@
- #include <linux/irq.h>
- #include <linux/irqchip/chained_irq.h>
- #include <linux/irqdomain.h>
- +#include <linux/of_irq.h>
- #include <linux/of_pci.h>
- #include <linux/of_platform.h>
- #include <linux/spinlock.h>
-
- -#define MSI_MAX_IRQS 32
- -#define MSI_IBS_SHIFT 3
- -#define MSIR 4
- +#define MSI_IRQS_PER_MSIR 32
- +#define MSI_MSIR_OFFSET 4
- +
- +#define MSI_LS1043V1_1_IRQS_PER_MSIR 8
- +#define MSI_LS1043V1_1_MSIR_OFFSET 0x10
- +
- +struct ls_scfg_msi_cfg {
- + u32 ibs_shift; /* Shift of interrupt bit select */
- + u32 msir_irqs; /* The irq number per MSIR */
- + u32 msir_base; /* The base address of MSIR */
- +};
- +
- +struct ls_scfg_msir {
- + struct ls_scfg_msi *msi_data;
- + unsigned int index;
- + unsigned int gic_irq;
- + unsigned int bit_start;
- + unsigned int bit_end;
- + unsigned int srs; /* Shared interrupt register select */
- + void __iomem *reg;
- +};
-
- struct ls_scfg_msi {
- spinlock_t lock;
- @@ -32,8 +51,11 @@ struct ls_scfg_msi {
- struct irq_domain *msi_domain;
- void __iomem *regs;
- phys_addr_t msiir_addr;
- - int irq;
- - DECLARE_BITMAP(used, MSI_MAX_IRQS);
- + struct ls_scfg_msi_cfg *cfg;
- + u32 msir_num;
- + struct ls_scfg_msir *msir;
- + u32 irqs_num;
- + unsigned long *used;
- };
-
- static struct irq_chip ls_scfg_msi_irq_chip = {
- @@ -49,19 +71,56 @@ static struct msi_domain_info ls_scfg_ms
- .chip = &ls_scfg_msi_irq_chip,
- };
-
- +static int msi_affinity_flag = 1;
- +
- +static int __init early_parse_ls_scfg_msi(char *p)
- +{
- + if (p && strncmp(p, "no-affinity", 11) == 0)
- + msi_affinity_flag = 0;
- + else
- + msi_affinity_flag = 1;
- +
- + return 0;
- +}
- +early_param("lsmsi", early_parse_ls_scfg_msi);
- +
- static void ls_scfg_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
- {
- struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(data);
-
- msg->address_hi = upper_32_bits(msi_data->msiir_addr);
- msg->address_lo = lower_32_bits(msi_data->msiir_addr);
- - msg->data = data->hwirq << MSI_IBS_SHIFT;
- + msg->data = data->hwirq;
- +
- + if (msi_affinity_flag)
- + msg->data |= cpumask_first(data->common->affinity);
- }
-
- static int ls_scfg_msi_set_affinity(struct irq_data *irq_data,
- const struct cpumask *mask, bool force)
- {
- - return -EINVAL;
- + struct ls_scfg_msi *msi_data = irq_data_get_irq_chip_data(irq_data);
- + u32 cpu;
- +
- + if (!msi_affinity_flag)
- + return -EINVAL;
- +
- + if (!force)
- + cpu = cpumask_any_and(mask, cpu_online_mask);
- + else
- + cpu = cpumask_first(mask);
- +
- + if (cpu >= msi_data->msir_num)
- + return -EINVAL;
- +
- + if (msi_data->msir[cpu].gic_irq <= 0) {
- + pr_warn("cannot bind the irq to cpu%d\n", cpu);
- + return -EINVAL;
- + }
- +
- + cpumask_copy(irq_data->common->affinity, mask);
- +
- + return IRQ_SET_MASK_OK;
- }
-
- static struct irq_chip ls_scfg_msi_parent_chip = {
- @@ -81,8 +140,8 @@ static int ls_scfg_msi_domain_irq_alloc(
- WARN_ON(nr_irqs != 1);
-
- spin_lock(&msi_data->lock);
- - pos = find_first_zero_bit(msi_data->used, MSI_MAX_IRQS);
- - if (pos < MSI_MAX_IRQS)
- + pos = find_first_zero_bit(msi_data->used, msi_data->irqs_num);
- + if (pos < msi_data->irqs_num)
- __set_bit(pos, msi_data->used);
- else
- err = -ENOSPC;
- @@ -106,7 +165,7 @@ static void ls_scfg_msi_domain_irq_free(
- int pos;
-
- pos = d->hwirq;
- - if (pos < 0 || pos >= MSI_MAX_IRQS) {
- + if (pos < 0 || pos >= msi_data->irqs_num) {
- pr_err("failed to teardown msi. Invalid hwirq %d\n", pos);
- return;
- }
- @@ -123,15 +182,22 @@ static const struct irq_domain_ops ls_sc
-
- static void ls_scfg_msi_irq_handler(struct irq_desc *desc)
- {
- - struct ls_scfg_msi *msi_data = irq_desc_get_handler_data(desc);
- + struct ls_scfg_msir *msir = irq_desc_get_handler_data(desc);
- + struct ls_scfg_msi *msi_data = msir->msi_data;
- unsigned long val;
- - int pos, virq;
- + int pos, size, virq, hwirq;
-
- chained_irq_enter(irq_desc_get_chip(desc), desc);
-
- - val = ioread32be(msi_data->regs + MSIR);
- - for_each_set_bit(pos, &val, MSI_MAX_IRQS) {
- - virq = irq_find_mapping(msi_data->parent, (31 - pos));
- + val = ioread32be(msir->reg);
- +
- + pos = msir->bit_start;
- + size = msir->bit_end + 1;
- +
- + for_each_set_bit_from(pos, &val, size) {
- + hwirq = ((msir->bit_end - pos) << msi_data->cfg->ibs_shift) |
- + msir->srs;
- + virq = irq_find_mapping(msi_data->parent, hwirq);
- if (virq)
- generic_handle_irq(virq);
- }
- @@ -143,7 +209,7 @@ static int ls_scfg_msi_domains_init(stru
- {
- /* Initialize MSI domain parent */
- msi_data->parent = irq_domain_add_linear(NULL,
- - MSI_MAX_IRQS,
- + msi_data->irqs_num,
- &ls_scfg_msi_domain_ops,
- msi_data);
- if (!msi_data->parent) {
- @@ -164,16 +230,118 @@ static int ls_scfg_msi_domains_init(stru
- return 0;
- }
-
- +static int ls_scfg_msi_setup_hwirq(struct ls_scfg_msi *msi_data, int index)
- +{
- + struct ls_scfg_msir *msir;
- + int virq, i, hwirq;
- +
- + virq = platform_get_irq(msi_data->pdev, index);
- + if (virq <= 0)
- + return -ENODEV;
- +
- + msir = &msi_data->msir[index];
- + msir->index = index;
- + msir->msi_data = msi_data;
- + msir->gic_irq = virq;
- + msir->reg = msi_data->regs + msi_data->cfg->msir_base + 4 * index;
- +
- + if (msi_data->cfg->msir_irqs == MSI_LS1043V1_1_IRQS_PER_MSIR) {
- + msir->bit_start = 32 - ((msir->index + 1) *
- + MSI_LS1043V1_1_IRQS_PER_MSIR);
- + msir->bit_end = msir->bit_start +
- + MSI_LS1043V1_1_IRQS_PER_MSIR - 1;
- + } else {
- + msir->bit_start = 0;
- + msir->bit_end = msi_data->cfg->msir_irqs - 1;
- + }
- +
- + irq_set_chained_handler_and_data(msir->gic_irq,
- + ls_scfg_msi_irq_handler,
- + msir);
- +
- + if (msi_affinity_flag) {
- + /* Associate MSIR interrupt to the cpu */
- + irq_set_affinity(msir->gic_irq, get_cpu_mask(index));
- + msir->srs = 0; /* This value is determined by the CPU */
- + } else
- + msir->srs = index;
- +
- + /* Release the hwirqs corresponding to this MSIR */
- + if (!msi_affinity_flag || msir->index == 0) {
- + for (i = 0; i < msi_data->cfg->msir_irqs; i++) {
- + hwirq = i << msi_data->cfg->ibs_shift | msir->index;
- + bitmap_clear(msi_data->used, hwirq, 1);
- + }
- + }
- +
- + return 0;
- +}
- +
- +static int ls_scfg_msi_teardown_hwirq(struct ls_scfg_msir *msir)
- +{
- + struct ls_scfg_msi *msi_data = msir->msi_data;
- + int i, hwirq;
- +
- + if (msir->gic_irq > 0)
- + irq_set_chained_handler_and_data(msir->gic_irq, NULL, NULL);
- +
- + for (i = 0; i < msi_data->cfg->msir_irqs; i++) {
- + hwirq = i << msi_data->cfg->ibs_shift | msir->index;
- + bitmap_set(msi_data->used, hwirq, 1);
- + }
- +
- + return 0;
- +}
- +
- +static struct ls_scfg_msi_cfg ls1021_msi_cfg = {
- + .ibs_shift = 3,
- + .msir_irqs = MSI_IRQS_PER_MSIR,
- + .msir_base = MSI_MSIR_OFFSET,
- +};
- +
- +static struct ls_scfg_msi_cfg ls1046_msi_cfg = {
- + .ibs_shift = 2,
- + .msir_irqs = MSI_IRQS_PER_MSIR,
- + .msir_base = MSI_MSIR_OFFSET,
- +};
- +
- +static struct ls_scfg_msi_cfg ls1043_v1_1_msi_cfg = {
- + .ibs_shift = 2,
- + .msir_irqs = MSI_LS1043V1_1_IRQS_PER_MSIR,
- + .msir_base = MSI_LS1043V1_1_MSIR_OFFSET,
- +};
- +
- +static const struct of_device_id ls_scfg_msi_id[] = {
- + /* The following two misspelled compatibles are obsolete */
- + { .compatible = "fsl,1s1021a-msi", .data = &ls1021_msi_cfg},
- + { .compatible = "fsl,1s1043a-msi", .data = &ls1021_msi_cfg},
- +
- + { .compatible = "fsl,ls1012a-msi", .data = &ls1021_msi_cfg },
- + { .compatible = "fsl,ls1021a-msi", .data = &ls1021_msi_cfg },
- + { .compatible = "fsl,ls1043a-msi", .data = &ls1021_msi_cfg },
- + { .compatible = "fsl,ls1043a-v1.1-msi", .data = &ls1043_v1_1_msi_cfg },
- + { .compatible = "fsl,ls1046a-msi", .data = &ls1046_msi_cfg },
- + {},
- +};
- +MODULE_DEVICE_TABLE(of, ls_scfg_msi_id);
- +
- static int ls_scfg_msi_probe(struct platform_device *pdev)
- {
- + const struct of_device_id *match;
- struct ls_scfg_msi *msi_data;
- struct resource *res;
- - int ret;
- + int i, ret;
- +
- + match = of_match_device(ls_scfg_msi_id, &pdev->dev);
- + if (!match)
- + return -ENODEV;
-
- msi_data = devm_kzalloc(&pdev->dev, sizeof(*msi_data), GFP_KERNEL);
- if (!msi_data)
- return -ENOMEM;
-
- + msi_data->cfg = (struct ls_scfg_msi_cfg *) match->data;
- +
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- msi_data->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(msi_data->regs)) {
- @@ -182,23 +350,48 @@ static int ls_scfg_msi_probe(struct plat
- }
- msi_data->msiir_addr = res->start;
-
- - msi_data->irq = platform_get_irq(pdev, 0);
- - if (msi_data->irq <= 0) {
- - dev_err(&pdev->dev, "failed to get MSI irq\n");
- - return -ENODEV;
- - }
- -
- msi_data->pdev = pdev;
- spin_lock_init(&msi_data->lock);
-
- + msi_data->irqs_num = MSI_IRQS_PER_MSIR *
- + (1 << msi_data->cfg->ibs_shift);
- + msi_data->used = devm_kcalloc(&pdev->dev,
- + BITS_TO_LONGS(msi_data->irqs_num),
- + sizeof(*msi_data->used),
- + GFP_KERNEL);
- + if (!msi_data->used)
- + return -ENOMEM;
- + /*
- + * Reserve all the hwirqs
- + * The available hwirqs will be released in ls1_msi_setup_hwirq()
- + */
- + bitmap_set(msi_data->used, 0, msi_data->irqs_num);
- +
- + msi_data->msir_num = of_irq_count(pdev->dev.of_node);
- +
- + if (msi_affinity_flag) {
- + u32 cpu_num;
- +
- + cpu_num = num_possible_cpus();
- + if (msi_data->msir_num >= cpu_num)
- + msi_data->msir_num = cpu_num;
- + else
- + msi_affinity_flag = 0;
- + }
- +
- + msi_data->msir = devm_kcalloc(&pdev->dev, msi_data->msir_num,
- + sizeof(*msi_data->msir),
- + GFP_KERNEL);
- + if (!msi_data->msir)
- + return -ENOMEM;
- +
- + for (i = 0; i < msi_data->msir_num; i++)
- + ls_scfg_msi_setup_hwirq(msi_data, i);
- +
- ret = ls_scfg_msi_domains_init(msi_data);
- if (ret)
- return ret;
-
- - irq_set_chained_handler_and_data(msi_data->irq,
- - ls_scfg_msi_irq_handler,
- - msi_data);
- -
- platform_set_drvdata(pdev, msi_data);
-
- return 0;
- @@ -207,8 +400,10 @@ static int ls_scfg_msi_probe(struct plat
- static int ls_scfg_msi_remove(struct platform_device *pdev)
- {
- struct ls_scfg_msi *msi_data = platform_get_drvdata(pdev);
- + int i;
-
- - irq_set_chained_handler_and_data(msi_data->irq, NULL, NULL);
- + for (i = 0; i < msi_data->msir_num; i++)
- + ls_scfg_msi_teardown_hwirq(&msi_data->msir[i]);
-
- irq_domain_remove(msi_data->msi_domain);
- irq_domain_remove(msi_data->parent);
- @@ -218,12 +413,6 @@ static int ls_scfg_msi_remove(struct pla
- return 0;
- }
-
- -static const struct of_device_id ls_scfg_msi_id[] = {
- - { .compatible = "fsl,1s1021a-msi", },
- - { .compatible = "fsl,1s1043a-msi", },
- - {},
- -};
- -
- static struct platform_driver ls_scfg_msi_driver = {
- .driver = {
- .name = "ls-scfg-msi",
- --- a/drivers/pci/host/Makefile
- +++ b/drivers/pci/host/Makefile
- @@ -17,7 +17,7 @@ obj-$(CONFIG_PCIE_XILINX) += pcie-xilinx
- obj-$(CONFIG_PCIE_XILINX_NWL) += pcie-xilinx-nwl.o
- obj-$(CONFIG_PCI_XGENE) += pci-xgene.o
- obj-$(CONFIG_PCI_XGENE_MSI) += pci-xgene-msi.o
- -obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o
- +obj-$(CONFIG_PCI_LAYERSCAPE) += pci-layerscape.o pci-layerscape-ep.o pci-layerscape-ep-debugfs.o
- obj-$(CONFIG_PCI_VERSATILE) += pci-versatile.o
- obj-$(CONFIG_PCIE_IPROC) += pcie-iproc.o
- obj-$(CONFIG_PCIE_IPROC_MSI) += pcie-iproc-msi.o
- --- /dev/null
- +++ b/drivers/pci/host/pci-layerscape-ep-debugfs.c
- @@ -0,0 +1,758 @@
- +/*
- + * PCIe Endpoint driver for Freescale Layerscape SoCs
- + *
- + * Copyright (C) 2015 Freescale Semiconductor.
- + *
- + * Author: Minghuan Lian <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License version 2 as
- + * published by the Free Software Foundation.
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/module.h>
- +#include <linux/debugfs.h>
- +#include <linux/time.h>
- +#include <linux/uaccess.h>
- +#include <linux/kthread.h>
- +#include <linux/slab.h>
- +#include <linux/dmaengine.h>
- +#include <linux/dma-mapping.h>
- +#include <linux/freezer.h>
- +
- +#include <linux/completion.h>
- +
- +#include "pci-layerscape-ep.h"
- +
- +#define PCIE_ATU_INDEX3 (0x3 << 0)
- +#define PCIE_ATU_INDEX2 (0x2 << 0)
- +#define PCIE_ATU_INDEX1 (0x1 << 0)
- +#define PCIE_ATU_INDEX0 (0x0 << 0)
- +
- +#define PCIE_BAR0_SIZE (4 * 1024) /* 4K */
- +#define PCIE_BAR1_SIZE (8 * 1024) /* 8K for MSIX */
- +#define PCIE_BAR2_SIZE (4 * 1024) /* 4K */
- +#define PCIE_BAR4_SIZE (1 * 1024 * 1024) /* 1M */
- +#define PCIE_MSI_OB_SIZE (4 * 1024) /* 4K */
- +
- +#define PCIE_MSI_MSG_ADDR_OFF 0x54
- +#define PCIE_MSI_MSG_DATA_OFF 0x5c
- +
- +enum test_type {
- + TEST_TYPE_DMA,
- + TEST_TYPE_MEMCPY
- +};
- +
- +enum test_dirt {
- + TEST_DIRT_READ,
- + TEST_DIRT_WRITE
- +};
- +
- +enum test_status {
- + TEST_IDLE,
- + TEST_BUSY
- +};
- +
- +struct ls_ep_test {
- + struct ls_ep_dev *ep;
- + void __iomem *cfg;
- + void __iomem *buf;
- + void __iomem *out;
- + void __iomem *msi;
- + dma_addr_t cfg_addr;
- + dma_addr_t buf_addr;
- + dma_addr_t out_addr;
- + dma_addr_t bus_addr;
- + dma_addr_t msi_addr;
- + u64 msi_msg_addr;
- + u16 msi_msg_data;
- + struct task_struct *thread;
- + spinlock_t lock;
- + struct completion done;
- + u32 len;
- + int loop;
- + char data;
- + enum test_dirt dirt;
- + enum test_type type;
- + enum test_status status;
- + u64 result; /* Mbps */
- + char cmd[256];
- +};
- +
- +static int ls_pcie_ep_trigger_msi(struct ls_ep_test *test)
- +{
- + if (!test->msi)
- + return -EINVAL;
- +
- + iowrite32(test->msi_msg_data, test->msi);
- +
- + return 0;
- +}
- +
- +static int ls_pcie_ep_test_try_run(struct ls_ep_test *test)
- +{
- + int ret;
- +
- + spin_lock(&test->lock);
- + if (test->status == TEST_IDLE) {
- + test->status = TEST_BUSY;
- + ret = 0;
- + } else
- + ret = -EBUSY;
- + spin_unlock(&test->lock);
- +
- + return ret;
- +}
- +
- +static void ls_pcie_ep_test_done(struct ls_ep_test *test)
- +{
- + spin_lock(&test->lock);
- + test->status = TEST_IDLE;
- + spin_unlock(&test->lock);
- +}
- +
- +static void ls_pcie_ep_test_dma_cb(void *arg)
- +{
- + struct ls_ep_test *test = arg;
- +
- + complete(&test->done);
- +}
- +
- +static int ls_pcie_ep_test_dma(struct ls_ep_test *test)
- +{
- + dma_cap_mask_t mask;
- + struct dma_chan *chan;
- + struct dma_device *dma_dev;
- + dma_addr_t src, dst;
- + enum dma_data_direction direction;
- + enum dma_ctrl_flags dma_flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
- + struct timespec start, end, period;
- + int i = 0;
- +
- + dma_cap_zero(mask);
- + dma_cap_set(DMA_MEMCPY, mask);
- +
- + chan = dma_request_channel(mask, NULL, test);
- + if (!chan) {
- + pr_err("failed to request dma channel\n");
- + return -EINVAL;
- + }
- +
- + memset(test->buf, test->data, test->len);
- +
- + if (test->dirt == TEST_DIRT_WRITE) {
- + src = test->buf_addr;
- + dst = test->out_addr;
- + direction = DMA_TO_DEVICE;
- + } else {
- + src = test->out_addr;
- + dst = test->buf_addr;
- + direction = DMA_FROM_DEVICE;
- + }
- +
- + dma_dev = chan->device;
- + dma_flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
- +
- + dma_sync_single_for_device(&test->ep->dev, test->buf_addr,
- + test->len, direction);
- +
- + set_freezable();
- +
- + getrawmonotonic(&start);
- + while (!kthread_should_stop() && (i < test->loop)) {
- + struct dma_async_tx_descriptor *dma_desc;
- + dma_cookie_t dma_cookie = {0};
- + unsigned long tmo;
- + int status;
- +
- + init_completion(&test->done);
- +
- + dma_desc = dma_dev->device_prep_dma_memcpy(chan,
- + dst, src,
- + test->len,
- + dma_flags);
- + if (!dma_desc) {
- + pr_err("DMA desc constr failed...\n");
- + goto _err;
- + }
- +
- + dma_desc->callback = ls_pcie_ep_test_dma_cb;
- + dma_desc->callback_param = test;
- + dma_cookie = dmaengine_submit(dma_desc);
- +
- + if (dma_submit_error(dma_cookie)) {
- + pr_err("DMA submit error....\n");
- + goto _err;
- + }
- +
- + /* Trigger the transaction */
- + dma_async_issue_pending(chan);
- +
- + tmo = wait_for_completion_timeout(&test->done,
- + msecs_to_jiffies(5 * test->len));
- + if (tmo == 0) {
- + pr_err("Self-test copy timed out, disabling\n");
- + goto _err;
- + }
- +
- + status = dma_async_is_tx_complete(chan, dma_cookie,
- + NULL, NULL);
- + if (status != DMA_COMPLETE) {
- + pr_err("got completion callback, but status is %s\n",
- + status == DMA_ERROR ? "error" : "in progress");
- + goto _err;
- + }
- +
- + i++;
- + }
- +
- + getrawmonotonic(&end);
- + period = timespec_sub(end, start);
- + test->result = test->len * 8ULL * i * 1000;
- + do_div(test->result, period.tv_sec * 1000 * 1000 * 1000 + period.tv_nsec);
- + dma_release_channel(chan);
- +
- + return 0;
- +
- +_err:
- + dma_release_channel(chan);
- + test->result = 0;
- + return -EINVAL;
- +}
- +
- +static int ls_pcie_ep_test_cpy(struct ls_ep_test *test)
- +{
- + void *dst, *src;
- + struct timespec start, end, period;
- + int i = 0;
- +
- + memset(test->buf, test->data, test->len);
- +
- + if (test->dirt == TEST_DIRT_WRITE) {
- + dst = test->out;
- + src = test->buf;
- + } else {
- + dst = test->buf;
- + src = test->out;
- + }
- +
- + getrawmonotonic(&start);
- + while (!kthread_should_stop() && i < test->loop) {
- + memcpy(dst, src, test->len);
- + i++;
- + }
- + getrawmonotonic(&end);
- +
- + period = timespec_sub(end, start);
- + test->result = test->len * 8ULL * i * 1000;
- + do_div(test->result, period.tv_sec * 1000 * 1000 * 1000 + period.tv_nsec);
- +
- + return 0;
- +}
- +
- +int ls_pcie_ep_test_thread(void *arg)
- +{
- + int ret;
- +
- + struct ls_ep_test *test = arg;
- +
- + if (test->type == TEST_TYPE_DMA)
- + ret = ls_pcie_ep_test_dma(test);
- + else
- + ret = ls_pcie_ep_test_cpy(test);
- +
- + if (ret) {
- + pr_err("\n%s \ttest failed\n",
- + test->cmd);
- + test->result = 0;
- + } else
- + pr_err("\n%s \tthroughput:%lluMbps\n",
- + test->cmd, test->result);
- +
- + ls_pcie_ep_test_done(test);
- +
- + ls_pcie_ep_trigger_msi(test);
- +
- + do_exit(0);
- +}
- +
- +static int ls_pcie_ep_free_test(struct ls_ep_dev *ep)
- +{
- + struct ls_ep_test *test = ep->driver_data;
- +
- + if (!test)
- + return 0;
- +
- + if (test->status == TEST_BUSY) {
- + kthread_stop(test->thread);
- + dev_info(&ep->dev,
- + "test is running please wait and run again\n");
- + return -EBUSY;
- + }
- +
- + if (test->buf)
- + free_pages((unsigned long)test->buf,
- + get_order(PCIE_BAR4_SIZE));
- +
- + if (test->cfg)
- + free_pages((unsigned long)test->cfg,
- + get_order(PCIE_BAR2_SIZE));
- +
- + if (test->out)
- + iounmap(test->out);
- +
- + kfree(test);
- + ep->driver_data = NULL;
- +
- + return 0;
- +}
- +
- +static int ls_pcie_ep_init_test(struct ls_ep_dev *ep, u64 bus_addr)
- +{
- + struct ls_pcie *pcie = ep->pcie;
- + struct ls_ep_test *test = ep->driver_data;
- + int err;
- +
- + if (test) {
- + dev_info(&ep->dev,
- + "Please use 'free' to remove the exiting test\n");
- + return -EBUSY;
- + }
- +
- + test = kzalloc(sizeof(*test), GFP_KERNEL);
- + if (!test)
- + return -ENOMEM;
- + ep->driver_data = test;
- + test->ep = ep;
- + spin_lock_init(&test->lock);
- + test->status = TEST_IDLE;
- +
- + test->buf = dma_alloc_coherent(pcie->dev, get_order(PCIE_BAR4_SIZE),
- + &test->buf_addr,
- + GFP_KERNEL);
- + if (!test->buf) {
- + dev_info(&ep->dev, "failed to get mem for bar4\n");
- + err = -ENOMEM;
- + goto _err;
- + }
- +
- + test->cfg = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
- + get_order(PCIE_BAR2_SIZE));
- + if (!test->cfg) {
- + dev_info(&ep->dev, "failed to get mem for bar4\n");
- + err = -ENOMEM;
- + goto _err;
- + }
- + test->cfg_addr = virt_to_phys(test->cfg);
- +
- + test->out_addr = pcie->out_base;
- + test->out = ioremap(test->out_addr, PCIE_BAR4_SIZE);
- + if (!test->out) {
- + dev_info(&ep->dev, "failed to map out\n");
- + err = -ENOMEM;
- + goto _err;
- + }
- +
- + test->bus_addr = bus_addr;
- +
- + test->msi_addr = test->out_addr + PCIE_BAR4_SIZE;
- + test->msi = ioremap(test->msi_addr, PCIE_MSI_OB_SIZE);
- + if (!test->msi)
- + dev_info(&ep->dev, "failed to map MSI outbound region\n");
- +
- + test->msi_msg_addr = ioread32(pcie->dbi + PCIE_MSI_MSG_ADDR_OFF) |
- + (((u64)ioread32(pcie->dbi + PCIE_MSI_MSG_ADDR_OFF + 4)) << 32);
- + test->msi_msg_data = ioread16(pcie->dbi + PCIE_MSI_MSG_DATA_OFF);
- +
- + ls_pcie_ep_dev_cfg_enable(ep);
- +
- + /* outbound iATU for memory */
- + ls_pcie_iatu_outbound_set(pcie, 0, PCIE_ATU_TYPE_MEM,
- + test->out_addr, bus_addr, PCIE_BAR4_SIZE);
- + /* outbound iATU for MSI */
- + ls_pcie_iatu_outbound_set(pcie, 1, PCIE_ATU_TYPE_MEM,
- + test->msi_addr, test->msi_msg_addr,
- + PCIE_MSI_OB_SIZE);
- +
- + /* ATU 0 : INBOUND : map BAR0 */
- + ls_pcie_iatu_inbound_set(pcie, 0, 0, test->cfg_addr);
- + /* ATU 2 : INBOUND : map BAR2 */
- + ls_pcie_iatu_inbound_set(pcie, 2, 2, test->cfg_addr);
- + /* ATU 3 : INBOUND : map BAR4 */
- + ls_pcie_iatu_inbound_set(pcie, 3, 4, test->buf_addr);
- +
- + return 0;
- +
- +_err:
- + ls_pcie_ep_free_test(ep);
- + return err;
- +}
- +
- +static int ls_pcie_ep_start_test(struct ls_ep_dev *ep, char *cmd)
- +{
- + struct ls_ep_test *test = ep->driver_data;
- + enum test_type type;
- + enum test_dirt dirt;
- + u32 cnt, len, loop;
- + unsigned int data;
- + char dirt_str[2];
- + int ret;
- +
- + if (strncmp(cmd, "dma", 3) == 0)
- + type = TEST_TYPE_DMA;
- + else
- + type = TEST_TYPE_MEMCPY;
- +
- + cnt = sscanf(&cmd[4], "%1s %u %u %x", dirt_str, &len, &loop, &data);
- + if (cnt != 4) {
- + dev_info(&ep->dev, "format error %s", cmd);
- + dev_info(&ep->dev, "dma/cpy <r/w> <packet_size> <loop> <data>\n");
- + return -EINVAL;
- + }
- +
- + if (strncmp(dirt_str, "r", 1) == 0)
- + dirt = TEST_DIRT_READ;
- + else
- + dirt = TEST_DIRT_WRITE;
- +
- + if (len > PCIE_BAR4_SIZE) {
- + dev_err(&ep->dev, "max len is %d", PCIE_BAR4_SIZE);
- + return -EINVAL;
- + }
- +
- + if (!test) {
- + dev_err(&ep->dev, "Please first run init command\n");
- + return -EINVAL;
- + }
- +
- + if (ls_pcie_ep_test_try_run(test)) {
- + dev_err(&ep->dev, "There is already a test running\n");
- + return -EINVAL;
- + }
- +
- + test->len = len;
- + test->loop = loop;
- + test->type = type;
- + test->data = (char)data;
- + test->dirt = dirt;
- + strcpy(test->cmd, cmd);
- + test->thread = kthread_run(ls_pcie_ep_test_thread, test,
- + "pcie ep test");
- + if (IS_ERR(test->thread)) {
- + dev_err(&ep->dev, "fork failed for pcie ep test\n");
- + ls_pcie_ep_test_done(test);
- + ret = PTR_ERR(test->thread);
- + }
- +
- + return ret;
- +}
- +
- +
- +/**
- + * ls_pcie_reg_ops_read - read for regs data
- + * @filp: the opened file
- + * @buffer: where to write the data for the user to read
- + * @count: the size of the user's buffer
- + * @ppos: file position offset
- + **/
- +static ssize_t ls_pcie_ep_dbg_regs_read(struct file *filp, char __user *buffer,
- + size_t count, loff_t *ppos)
- +{
- + struct ls_ep_dev *ep = filp->private_data;
- + struct ls_pcie *pcie = ep->pcie;
- + char *buf;
- + int desc = 0, i, len;
- +
- + buf = kmalloc(4 * 1024, GFP_KERNEL);
- + if (!buf)
- + return -ENOMEM;
- +
- + ls_pcie_ep_dev_cfg_enable(ep);
- +
- + desc += sprintf(buf + desc, "%s", "reg info:");
- + for (i = 0; i < 0x200; i += 4) {
- + if (i % 16 == 0)
- + desc += sprintf(buf + desc, "\n%08x:", i);
- + desc += sprintf(buf + desc, " %08x", readl(pcie->dbi + i));
- + }
- +
- + desc += sprintf(buf + desc, "\n%s", "outbound iATU info:\n");
- + for (i = 0; i < 6; i++) {
- + writel(PCIE_ATU_REGION_OUTBOUND | i,
- + pcie->dbi + PCIE_ATU_VIEWPORT);
- + desc += sprintf(buf + desc, "iATU%d", i);
- + desc += sprintf(buf + desc, "\tLOWER PHYS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_LOWER_BASE));
- + desc += sprintf(buf + desc, "\tUPPER PHYS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_UPPER_BASE));
- + desc += sprintf(buf + desc, "\tLOWER BUS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_LOWER_TARGET));
- + desc += sprintf(buf + desc, "\tUPPER BUS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_UPPER_TARGET));
- + desc += sprintf(buf + desc, "\tLIMIT 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_LIMIT));
- + desc += sprintf(buf + desc, "\tCR1 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_CR1));
- + desc += sprintf(buf + desc, "\tCR2 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_CR2));
- + }
- +
- + desc += sprintf(buf + desc, "\n%s", "inbound iATU info:\n");
- + for (i = 0; i < 6; i++) {
- + writel(PCIE_ATU_REGION_INBOUND | i,
- + pcie->dbi + PCIE_ATU_VIEWPORT);
- + desc += sprintf(buf + desc, "iATU%d", i);
- + desc += sprintf(buf + desc, "\tLOWER BUS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_LOWER_BASE));
- + desc += sprintf(buf + desc, "\tUPPER BUSs 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_UPPER_BASE));
- + desc += sprintf(buf + desc, "\tLOWER PHYS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_LOWER_TARGET));
- + desc += sprintf(buf + desc, "\tUPPER PHYS 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_UPPER_TARGET));
- + desc += sprintf(buf + desc, "\tLIMIT 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_LIMIT));
- + desc += sprintf(buf + desc, "\tCR1 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_CR1));
- + desc += sprintf(buf + desc, "\tCR2 0x%08x\n",
- + readl(pcie->dbi + PCIE_ATU_CR2));
- + }
- +
- + len = simple_read_from_buffer(buffer, count, ppos, buf, desc);
- + kfree(buf);
- +
- + return len;
- +}
- +
- +/**
- + * ls_pcie_ep_dbg_regs_write - write into regs datum
- + * @filp: the opened file
- + * @buffer: where to find the user's data
- + * @count: the length of the user's data
- + * @ppos: file position offset
- + **/
- +static ssize_t ls_pcie_ep_dbg_regs_write(struct file *filp,
- + const char __user *buffer,
- + size_t count, loff_t *ppos)
- +{
- + struct ls_ep_dev *ep = filp->private_data;
- + struct ls_pcie *pcie = ep->pcie;
- + char buf[256];
- +
- + if (count >= sizeof(buf))
- + return -ENOSPC;
- +
- + memset(buf, 0, sizeof(buf));
- +
- + if (copy_from_user(buf, buffer, count))
- + return -EFAULT;
- +
- + ls_pcie_ep_dev_cfg_enable(ep);
- +
- + if (strncmp(buf, "reg", 3) == 0) {
- + u32 reg, value;
- + int cnt;
- +
- + cnt = sscanf(&buf[3], "%x %x", ®, &value);
- + if (cnt == 2) {
- + writel(value, pcie->dbi + reg);
- + value = readl(pcie->dbi + reg);
- + dev_info(&ep->dev, "reg 0x%08x: 0x%08x\n",
- + reg, value);
- + } else {
- + dev_info(&ep->dev, "reg <reg> <value>\n");
- + }
- + } else if (strncmp(buf, "atu", 3) == 0) {
- + /* to do */
- + dev_info(&ep->dev, " Not support atu command\n");
- + } else {
- + dev_info(&ep->dev, "Unknown command %s\n", buf);
- + dev_info(&ep->dev, "Available commands:\n");
- + dev_info(&ep->dev, " reg <reg> <value>\n");
- + }
- +
- + return count;
- +}
- +
- +static const struct file_operations ls_pcie_ep_dbg_regs_fops = {
- + .owner = THIS_MODULE,
- + .open = simple_open,
- + .read = ls_pcie_ep_dbg_regs_read,
- + .write = ls_pcie_ep_dbg_regs_write,
- +};
- +
- +static ssize_t ls_pcie_ep_dbg_test_read(struct file *filp,
- + char __user *buffer,
- + size_t count, loff_t *ppos)
- +{
- + struct ls_ep_dev *ep = filp->private_data;
- + struct ls_ep_test *test = ep->driver_data;
- + char buf[512];
- + int desc = 0, len;
- +
- + if (!test) {
- + dev_info(&ep->dev, " there is NO test\n");
- + return 0;
- + }
- +
- + if (test->status != TEST_IDLE) {
- + dev_info(&ep->dev, "test %s is running\n", test->cmd);
- + return 0;
- + }
- +
- + desc = sprintf(buf, "MSI ADDR:0x%llx MSI DATA:0x%x\n",
- + test->msi_msg_addr, test->msi_msg_data);
- +
- + desc += sprintf(buf + desc, "%s throughput:%lluMbps\n",
- + test->cmd, test->result);
- +
- + len = simple_read_from_buffer(buffer, count, ppos,
- + buf, desc);
- +
- + return len;
- +}
- +
- +static ssize_t ls_pcie_ep_dbg_test_write(struct file *filp,
- + const char __user *buffer,
- + size_t count, loff_t *ppos)
- +{
- + struct ls_ep_dev *ep = filp->private_data;
- + char buf[256];
- +
- + if (count >= sizeof(buf))
- + return -ENOSPC;
- +
- + memset(buf, 0, sizeof(buf));
- +
- + if (copy_from_user(buf, buffer, count))
- + return -EFAULT;
- +
- + if (strncmp(buf, "init", 4) == 0) {
- + int i = 4;
- + u64 bus_addr;
- +
- + while (buf[i] == ' ')
- + i++;
- +
- + if (kstrtou64(&buf[i], 0, &bus_addr))
- + dev_info(&ep->dev, "command: init <bus_addr>\n");
- + else {
- + if (ls_pcie_ep_init_test(ep, bus_addr))
- + dev_info(&ep->dev, "failed to init test\n");
- + }
- + } else if (strncmp(buf, "free", 4) == 0)
- + ls_pcie_ep_free_test(ep);
- + else if (strncmp(buf, "dma", 3) == 0 ||
- + strncmp(buf, "cpy", 3) == 0)
- + ls_pcie_ep_start_test(ep, buf);
- + else {
- + dev_info(&ep->dev, "Unknown command: %s\n", buf);
- + dev_info(&ep->dev, "Available commands:\n");
- + dev_info(&ep->dev, "\tinit <bus_addr>\n");
- + dev_info(&ep->dev, "\t<dma/cpy> <r/w> <packet_size> <loop>\n");
- + dev_info(&ep->dev, "\tfree\n");
- + }
- +
- + return count;
- +}
- +
- +static const struct file_operations ls_pcie_ep_dbg_test_fops = {
- + .owner = THIS_MODULE,
- + .open = simple_open,
- + .read = ls_pcie_ep_dbg_test_read,
- + .write = ls_pcie_ep_dbg_test_write,
- +};
- +
- +static ssize_t ls_pcie_ep_dbg_dump_read(struct file *filp,
- + char __user *buffer,
- + size_t count, loff_t *ppos)
- +{
- + struct ls_ep_dev *ep = filp->private_data;
- + struct ls_ep_test *test = ep->driver_data;
- + char *buf;
- + int desc = 0, i, len;
- +
- + buf = kmalloc(4 * 1024, GFP_KERNEL);
- + if (!buf)
- + return -ENOMEM;
- +
- + if (!test) {
- + dev_info(&ep->dev, " there is NO test\n");
- + kfree(buf);
- + return 0;
- + }
- +
- + desc += sprintf(buf + desc, "%s", "dump info:");
- + for (i = 0; i < 256; i += 4) {
- + if (i % 16 == 0)
- + desc += sprintf(buf + desc, "\n%08x:", i);
- + desc += sprintf(buf + desc, " %08x", readl(test->buf + i));
- + }
- +
- + desc += sprintf(buf + desc, "\n");
- + len = simple_read_from_buffer(buffer, count, ppos, buf, desc);
- +
- + kfree(buf);
- +
- + return len;
- +}
- +
- +static const struct file_operations ls_pcie_ep_dbg_dump_fops = {
- + .owner = THIS_MODULE,
- + .open = simple_open,
- + .read = ls_pcie_ep_dbg_dump_read,
- +};
- +
- +static int ls_pcie_ep_dev_dbgfs_init(struct ls_ep_dev *ep)
- +{
- + struct ls_pcie *pcie = ep->pcie;
- + struct dentry *pfile;
- +
- + ls_pcie_ep_dev_cfg_enable(ep);
- +
- + ep->dir = debugfs_create_dir(dev_name(&ep->dev), pcie->dir);
- + if (!ep->dir)
- + return -ENOMEM;
- +
- + pfile = debugfs_create_file("regs", 0600, ep->dir, ep,
- + &ls_pcie_ep_dbg_regs_fops);
- + if (!pfile)
- + dev_info(&ep->dev, "debugfs regs for failed\n");
- +
- + pfile = debugfs_create_file("test", 0600, ep->dir, ep,
- + &ls_pcie_ep_dbg_test_fops);
- + if (!pfile)
- + dev_info(&ep->dev, "debugfs test for failed\n");
- +
- + pfile = debugfs_create_file("dump", 0600, ep->dir, ep,
- + &ls_pcie_ep_dbg_dump_fops);
- + if (!pfile)
- + dev_info(&ep->dev, "debugfs dump for failed\n");
- +
- + return 0;
- +}
- +
- +int ls_pcie_ep_dbgfs_init(struct ls_pcie *pcie)
- +{
- + struct ls_ep_dev *ep;
- +
- + pcie->dir = debugfs_create_dir(dev_name(pcie->dev), NULL);
- + if (!pcie->dir)
- + return -ENOMEM;
- +
- + list_for_each_entry(ep, &pcie->ep_list, node)
- + ls_pcie_ep_dev_dbgfs_init(ep);
- +
- + return 0;
- +}
- +
- +int ls_pcie_ep_dbgfs_remove(struct ls_pcie *pcie)
- +{
- + debugfs_remove_recursive(pcie->dir);
- + return 0;
- +}
- +
- +MODULE_AUTHOR("Minghuan Lian <[email protected]>");
- +MODULE_DESCRIPTION("Freescale Layerscape PCIe EP controller driver");
- +MODULE_LICENSE("GPL v2");
- --- /dev/null
- +++ b/drivers/pci/host/pci-layerscape-ep.c
- @@ -0,0 +1,309 @@
- +/*
- + * PCIe Endpoint driver for Freescale Layerscape SoCs
- + *
- + * Copyright (C) 2015 Freescale Semiconductor.
- + *
- + * Author: Minghuan Lian <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License version 2 as
- + * published by the Free Software Foundation.
- + */
- +
- +#include <linux/kernel.h>
- +#include <linux/delay.h>
- +#include <linux/interrupt.h>
- +#include <linux/module.h>
- +#include <linux/of_pci.h>
- +#include <linux/of_platform.h>
- +#include <linux/of_irq.h>
- +#include <linux/of_address.h>
- +#include <linux/pci.h>
- +#include <linux/platform_device.h>
- +#include <linux/resource.h>
- +#include <linux/debugfs.h>
- +#include <linux/time.h>
- +#include <linux/uaccess.h>
- +
- +#include "pci-layerscape-ep.h"
- +
- +struct ls_ep_dev *
- +ls_pci_ep_find(struct ls_pcie *pcie, int dev_id)
- +{
- + struct ls_ep_dev *ep;
- +
- + list_for_each_entry(ep, &pcie->ep_list, node) {
- + if (ep->dev_id == dev_id)
- + return ep;
- + }
- +
- + return NULL;
- +}
- +
- +static void ls_pcie_try_cfg2(struct ls_pcie *pcie, int pf, int vf)
- +{
- + if (pcie->sriov)
- + writel(PCIE_LCTRL0_VAL(pf, vf),
- + pcie->dbi + PCIE_LUT_BASE + PCIE_LUT_LCTRL0);
- +}
- +
- +static bool ls_pcie_is_bridge(struct ls_pcie *pcie)
- +{
- + u32 header_type = 0;
- +
- + header_type = readl(pcie->dbi + (PCI_HEADER_TYPE & ~0x3));
- + header_type = (header_type >> 16) & 0x7f;
- +
- + return header_type == PCI_HEADER_TYPE_BRIDGE;
- +}
- +
- +void ls_pcie_iatu_outbound_set(struct ls_pcie *pcie, int idx, int type,
- + u64 cpu_addr, u64 pci_addr, u32 size)
- +{
- + writel(PCIE_ATU_REGION_OUTBOUND | idx,
- + pcie->dbi + PCIE_ATU_VIEWPORT);
- + writel(lower_32_bits(cpu_addr),
- + pcie->dbi + PCIE_ATU_LOWER_BASE);
- + writel(upper_32_bits(cpu_addr),
- + pcie->dbi + PCIE_ATU_UPPER_BASE);
- + writel(lower_32_bits(cpu_addr + size - 1),
- + pcie->dbi + PCIE_ATU_LIMIT);
- + writel(lower_32_bits(pci_addr),
- + pcie->dbi + PCIE_ATU_LOWER_TARGET);
- + writel(upper_32_bits(pci_addr),
- + pcie->dbi + PCIE_ATU_UPPER_TARGET);
- + writel(type, pcie->dbi + PCIE_ATU_CR1);
- + writel(PCIE_ATU_ENABLE, pcie->dbi + PCIE_ATU_CR2);
- +}
- +
- +/* Use bar match mode and MEM type as default */
- +void ls_pcie_iatu_inbound_set(struct ls_pcie *pcie, int idx,
- + int bar, u64 phys)
- +{
- + writel(PCIE_ATU_REGION_INBOUND | idx, pcie->dbi + PCIE_ATU_VIEWPORT);
- + writel((u32)phys, pcie->dbi + PCIE_ATU_LOWER_TARGET);
- + writel(phys >> 32, pcie->dbi + PCIE_ATU_UPPER_TARGET);
- + writel(PCIE_ATU_TYPE_MEM, pcie->dbi + PCIE_ATU_CR1);
- + writel(PCIE_ATU_ENABLE | PCIE_ATU_BAR_MODE_ENABLE |
- + PCIE_ATU_BAR_NUM(bar), pcie->dbi + PCIE_ATU_CR2);
- +}
- +
- +void ls_pcie_ep_dev_cfg_enable(struct ls_ep_dev *ep)
- +{
- + ls_pcie_try_cfg2(ep->pcie, ep->pf_idx, ep->vf_idx);
- +}
- +
- +void ls_pcie_ep_setup_bar(void *bar_base, int bar, u32 size)
- +{
- + if (size < 4 * 1024)
- + return;
- +
- + switch (bar) {
- + case 0:
- + writel(size - 1, bar_base + PCI_BASE_ADDRESS_0);
- + break;
- + case 1:
- + writel(size - 1, bar_base + PCI_BASE_ADDRESS_1);
- + break;
- + case 2:
- + writel(size - 1, bar_base + PCI_BASE_ADDRESS_2);
- + writel(0, bar_base + PCI_BASE_ADDRESS_3);
- + break;
- + case 4:
- + writel(size - 1, bar_base + PCI_BASE_ADDRESS_4);
- + writel(0, bar_base + PCI_BASE_ADDRESS_5);
- + break;
- + default:
- + break;
- + }
- +}
- +
- +void ls_pcie_ep_dev_setup_bar(struct ls_ep_dev *ep, int bar, u32 size)
- +{
- + struct ls_pcie *pcie = ep->pcie;
- + void *bar_base;
- +
- + if (size < 4 * 1024)
- + return;
- +
- + if (pcie->sriov)
- + bar_base = pcie->dbi;
- + else
- + bar_base = pcie->dbi + PCIE_NO_SRIOV_BAR_BASE;
- +
- + ls_pcie_ep_dev_cfg_enable(ep);
- + ls_pcie_ep_setup_bar(bar_base, bar, size);
- +}
- +
- +static int ls_pcie_ep_dev_init(struct ls_pcie *pcie, int pf_idx, int vf_idx)
- +{
- + struct ls_ep_dev *ep;
- +
- + ep = devm_kzalloc(pcie->dev, sizeof(*ep), GFP_KERNEL);
- + if (!ep)
- + return -ENOMEM;
- +
- + ep->pcie = pcie;
- + ep->pf_idx = pf_idx;
- + ep->vf_idx = vf_idx;
- + if (vf_idx)
- + ep->dev_id = pf_idx + 4 + 4 * (vf_idx - 1);
- + else
- + ep->dev_id = pf_idx;
- +
- + if (ep->vf_idx)
- + dev_set_name(&ep->dev, "pf%d-vf%d",
- + ep->pf_idx,
- + ep->vf_idx);
- + else
- + dev_set_name(&ep->dev, "pf%d",
- + ep->pf_idx);
- +
- + list_add_tail(&ep->node, &pcie->ep_list);
- +
- + return 0;
- +}
- +
- +static int ls_pcie_ep_init(struct ls_pcie *pcie)
- +{
- + u32 sriov_header;
- + int pf, vf, i, j;
- +
- + sriov_header = readl(pcie->dbi + PCIE_SRIOV_POS);
- +
- + if (PCI_EXT_CAP_ID(sriov_header) == PCI_EXT_CAP_ID_SRIOV) {
- + pcie->sriov = PCIE_SRIOV_POS;
- + pf = PCIE_PF_NUM;
- + vf = PCIE_VF_NUM;
- + } else {
- + pcie->sriov = 0;
- + pf = 1;
- + vf = 0;
- + }
- +
- + for (i = 0; i < pf; i++) {
- + for (j = 0; j <= vf; j++)
- + ls_pcie_ep_dev_init(pcie, i, j);
- + }
- +
- + return 0;
- +}
- +
- +static struct ls_pcie_ep_drvdata ls1043_drvdata = {
- + .lut_offset = 0x10000,
- + .ltssm_shift = 24,
- + .lut_dbg = 0x7fc,
- +};
- +
- +static struct ls_pcie_ep_drvdata ls1046_drvdata = {
- + .lut_offset = 0x80000,
- + .ltssm_shift = 24,
- + .lut_dbg = 0x407fc,
- +};
- +
- +static struct ls_pcie_ep_drvdata ls2080_drvdata = {
- + .lut_offset = 0x80000,
- + .ltssm_shift = 0,
- + .lut_dbg = 0x7fc,
- +};
- +
- +static const struct of_device_id ls_pcie_ep_of_match[] = {
- + { .compatible = "fsl,ls1021a-pcie", },
- + { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
- + { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
- + { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
- + { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
- + { },
- +};
- +MODULE_DEVICE_TABLE(of, ls_pcie_ep_of_match);
- +
- +static int ls_pcie_ep_probe(struct platform_device *pdev)
- +{
- + struct ls_pcie *pcie;
- + struct resource *dbi_base, *cfg_res;
- + const struct of_device_id *match;
- + int ret;
- +
- + match = of_match_device(ls_pcie_ep_of_match, &pdev->dev);
- + if (!match)
- + return -ENODEV;
- +
- + pcie = devm_kzalloc(&pdev->dev, sizeof(*pcie), GFP_KERNEL);
- + if (!pcie)
- + return -ENOMEM;
- +
- + pcie->dev = &pdev->dev;
- + INIT_LIST_HEAD(&pcie->ep_list);
- +
- + dbi_base = platform_get_resource_byname(pdev, IORESOURCE_MEM, "regs");
- + pcie->dbi = devm_ioremap_resource(&pdev->dev, dbi_base);
- + if (IS_ERR(pcie->dbi)) {
- + dev_err(&pdev->dev, "missing *regs* space\n");
- + return PTR_ERR(pcie->dbi);
- + }
- +
- + pcie->drvdata = match->data;
- + pcie->lut = pcie->dbi + pcie->drvdata->lut_offset;
- +
- + if (ls_pcie_is_bridge(pcie))
- + return -ENODEV;
- +
- + dev_info(pcie->dev, "in EP mode\n");
- +
- + cfg_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config");
- + if (cfg_res)
- + pcie->out_base = cfg_res->start;
- + else {
- + dev_err(&pdev->dev, "missing *config* space\n");
- + return -ENODEV;
- + }
- +
- + ret = ls_pcie_ep_init(pcie);
- + if (ret)
- + return ret;
- +
- + ls_pcie_ep_dbgfs_init(pcie);
- +
- + platform_set_drvdata(pdev, pcie);
- +
- + return 0;
- +}
- +
- +static int ls_pcie_ep_dev_remove(struct ls_ep_dev *ep)
- +{
- + list_del(&ep->node);
- +
- + return 0;
- +}
- +
- +static int ls_pcie_ep_remove(struct platform_device *pdev)
- +{
- + struct ls_pcie *pcie = platform_get_drvdata(pdev);
- + struct ls_ep_dev *ep, *tmp;
- +
- + if (!pcie)
- + return 0;
- +
- + ls_pcie_ep_dbgfs_remove(pcie);
- +
- + list_for_each_entry_safe(ep, tmp, &pcie->ep_list, node)
- + ls_pcie_ep_dev_remove(ep);
- +
- + return 0;
- +}
- +
- +static struct platform_driver ls_pcie_ep_driver = {
- + .driver = {
- + .name = "ls-pcie-ep",
- + .owner = THIS_MODULE,
- + .of_match_table = ls_pcie_ep_of_match,
- + },
- + .probe = ls_pcie_ep_probe,
- + .remove = ls_pcie_ep_remove,
- +};
- +
- +module_platform_driver(ls_pcie_ep_driver);
- +
- +MODULE_AUTHOR("Minghuan Lian <[email protected]>");
- +MODULE_DESCRIPTION("Freescale Layerscape PCIe EP driver");
- +MODULE_LICENSE("GPL v2");
- --- /dev/null
- +++ b/drivers/pci/host/pci-layerscape-ep.h
- @@ -0,0 +1,115 @@
- +/*
- + * PCIe Endpoint driver for Freescale Layerscape SoCs
- + *
- + * Copyright (C) 2015 Freescale Semiconductor.
- + *
- + * Author: Minghuan Lian <[email protected]>
- + *
- + * This program is free software; you can redistribute it and/or modify
- + * it under the terms of the GNU General Public License version 2 as
- + * published by the Free Software Foundation.
- + */
- +
- +
- +#ifndef _PCIE_LAYERSCAPE_EP_H
- +#define _PCIE_LAYERSCAPE_EP_H
- +
- +#include <linux/device.h>
- +
- +/* Synopsis specific PCIE configuration registers */
- +#define PCIE_ATU_VIEWPORT 0x900
- +#define PCIE_ATU_REGION_INBOUND (0x1 << 31)
- +#define PCIE_ATU_REGION_OUTBOUND (0x0 << 31)
- +#define PCIE_ATU_REGION_INDEX3 (0x3 << 0)
- +#define PCIE_ATU_REGION_INDEX2 (0x2 << 0)
- +#define PCIE_ATU_REGION_INDEX1 (0x1 << 0)
- +#define PCIE_ATU_REGION_INDEX0 (0x0 << 0)
- +#define PCIE_ATU_CR1 0x904
- +#define PCIE_ATU_TYPE_MEM (0x0 << 0)
- +#define PCIE_ATU_TYPE_IO (0x2 << 0)
- +#define PCIE_ATU_TYPE_CFG0 (0x4 << 0)
- +#define PCIE_ATU_TYPE_CFG1 (0x5 << 0)
- +#define PCIE_ATU_CR2 0x908
- +#define PCIE_ATU_ENABLE (0x1 << 31)
- +#define PCIE_ATU_BAR_MODE_ENABLE (0x1 << 30)
- +#define PCIE_ATU_LOWER_BASE 0x90C
- +#define PCIE_ATU_UPPER_BASE 0x910
- +#define PCIE_ATU_LIMIT 0x914
- +#define PCIE_ATU_LOWER_TARGET 0x918
- +#define PCIE_ATU_BUS(x) (((x) & 0xff) << 24)
- +#define PCIE_ATU_DEV(x) (((x) & 0x1f) << 19)
- +#define PCIE_ATU_FUNC(x) (((x) & 0x7) << 16)
- +#define PCIE_ATU_UPPER_TARGET 0x91C
- +
- +/* PEX internal configuration registers */
- +#define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */
- +
- +/* PEX LUT registers */
- +#define PCIE_LUT_BASE 0x80000
- +#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug register */
- +
- +#define PCIE_LUT_LCTRL0 0x7F8
- +
- +#define PCIE_ATU_BAR_NUM(bar) ((bar) << 8)
- +#define PCIE_LCTRL0_CFG2_ENABLE (1 << 31)
- +#define PCIE_LCTRL0_VF(vf) ((vf) << 22)
- +#define PCIE_LCTRL0_PF(pf) ((pf) << 16)
- +#define PCIE_LCTRL0_VF_ACTIVE (1 << 21)
- +#define PCIE_LCTRL0_VAL(pf, vf) (PCIE_LCTRL0_PF(pf) | \
- + PCIE_LCTRL0_VF(vf) | \
- + ((vf) == 0 ? 0 : PCIE_LCTRL0_VF_ACTIVE) | \
- + PCIE_LCTRL0_CFG2_ENABLE)
- +
- +#define PCIE_NO_SRIOV_BAR_BASE 0x1000
- +
- +#define PCIE_SRIOV_POS 0x178
- +#define PCIE_PF_NUM 2
- +#define PCIE_VF_NUM 64
- +
- +struct ls_pcie_ep_drvdata {
- + u32 lut_offset;
- + u32 ltssm_shift;
- + u32 lut_dbg;
- +};
- +
- +struct ls_pcie {
- + struct list_head ep_list;
- + struct device *dev;
- + struct dentry *dir;
- + const struct ls_pcie_ep_drvdata *drvdata;
- + void __iomem *dbi;
- + void __iomem *lut;
- + phys_addr_t out_base;
- + int sriov;
- + int index;
- +};
- +
- +struct ls_ep_dev {
- + struct list_head node;
- + struct ls_pcie *pcie;
- + struct device dev;
- + struct dentry *dir;
- + int pf_idx;
- + int vf_idx;
- + int dev_id;
- + void *driver_data;
- +};
- +
- +struct ls_ep_dev *ls_pci_ep_find(struct ls_pcie *pcie, int dev_id);
- +
- +void ls_pcie_iatu_outbound_set(struct ls_pcie *pcie, int idx, int type,
- + u64 cpu_addr, u64 pci_addr, u32 size);
- +
- +/* Use bar match mode and MEM type as default */
- +void ls_pcie_iatu_inbound_set(struct ls_pcie *pcie, int idx,
- + int bar, u64 phys);
- +
- +void ls_pcie_ep_dev_setup_bar(struct ls_ep_dev *ep, int bar, u32 size);
- +
- +
- +void ls_pcie_ep_dev_cfg_enable(struct ls_ep_dev *ep);
- +
- +int ls_pcie_ep_dbgfs_init(struct ls_pcie *pcie);
- +int ls_pcie_ep_dbgfs_remove(struct ls_pcie *pcie);
- +
- +#endif /* _PCIE_LAYERSCAPE_EP_H */
- --- a/drivers/pci/host/pci-layerscape.c
- +++ b/drivers/pci/host/pci-layerscape.c
- @@ -33,14 +33,18 @@
-
- /* PEX Internal Configuration Registers */
- #define PCIE_STRFMR1 0x71c /* Symbol Timer & Filter Mask Register1 */
- +#define PCIE_ABSERR 0x8d0 /* Bridge Slave Error Response Register */
- +#define PCIE_ABSERR_SETTING 0x9401 /* Forward error of non-posted request */
- #define PCIE_DBI_RO_WR_EN 0x8bc /* DBI Read-Only Write Enable Register */
-
- -/* PEX LUT registers */
- -#define PCIE_LUT_DBG 0x7FC /* PEX LUT Debug Register */
- +#define PCIE_IATU_NUM 6
- +
- +static void ls_pcie_host_init(struct pcie_port *pp);
-
- struct ls_pcie_drvdata {
- u32 lut_offset;
- u32 ltssm_shift;
- + u32 lut_dbg;
- struct pcie_host_ops *ops;
- };
-
- @@ -86,6 +90,14 @@ static void ls_pcie_drop_msg_tlp(struct
- iowrite32(val, pcie->pp.dbi_base + PCIE_STRFMR1);
- }
-
- +static void ls_pcie_disable_outbound_atus(struct ls_pcie *pcie)
- +{
- + int i;
- +
- + for (i = 0; i < PCIE_IATU_NUM; i++)
- + dw_pcie_disable_outbound_atu(&pcie->pp, i);
- +}
- +
- static int ls1021_pcie_link_up(struct pcie_port *pp)
- {
- u32 state;
- @@ -134,7 +146,7 @@ static int ls_pcie_link_up(struct pcie_p
- struct ls_pcie *pcie = to_ls_pcie(pp);
- u32 state;
-
- - state = (ioread32(pcie->lut + PCIE_LUT_DBG) >>
- + state = (ioread32(pcie->lut + pcie->drvdata->lut_dbg) >>
- pcie->drvdata->ltssm_shift) &
- LTSSM_STATE_MASK;
-
- @@ -144,6 +156,12 @@ static int ls_pcie_link_up(struct pcie_p
- return 1;
- }
-
- +/* Forward error response of outbound non-posted requests */
- +static void ls_pcie_fix_error_response(struct ls_pcie *pcie)
- +{
- + iowrite32(PCIE_ABSERR_SETTING, pcie->pp.dbi_base + PCIE_ABSERR);
- +}
- +
- static void ls_pcie_host_init(struct pcie_port *pp)
- {
- struct ls_pcie *pcie = to_ls_pcie(pp);
- @@ -153,6 +171,10 @@ static void ls_pcie_host_init(struct pci
- ls_pcie_clear_multifunction(pcie);
- ls_pcie_drop_msg_tlp(pcie);
- iowrite32(0, pcie->pp.dbi_base + PCIE_DBI_RO_WR_EN);
- +
- + ls_pcie_disable_outbound_atus(pcie);
- + ls_pcie_fix_error_response(pcie);
- + dw_pcie_setup_rc(pp);
- }
-
- static int ls_pcie_msi_host_init(struct pcie_port *pp,
- @@ -196,20 +218,40 @@ static struct ls_pcie_drvdata ls1021_drv
- static struct ls_pcie_drvdata ls1043_drvdata = {
- .lut_offset = 0x10000,
- .ltssm_shift = 24,
- + .lut_dbg = 0x7fc,
- + .ops = &ls_pcie_host_ops,
- +};
- +
- +static struct ls_pcie_drvdata ls1046_drvdata = {
- + .lut_offset = 0x80000,
- + .ltssm_shift = 24,
- + .lut_dbg = 0x407fc,
- .ops = &ls_pcie_host_ops,
- };
-
- static struct ls_pcie_drvdata ls2080_drvdata = {
- .lut_offset = 0x80000,
- .ltssm_shift = 0,
- + .lut_dbg = 0x7fc,
- + .ops = &ls_pcie_host_ops,
- +};
- +
- +static struct ls_pcie_drvdata ls2088_drvdata = {
- + .lut_offset = 0x80000,
- + .ltssm_shift = 0,
- + .lut_dbg = 0x407fc,
- .ops = &ls_pcie_host_ops,
- };
-
- static const struct of_device_id ls_pcie_of_match[] = {
- + { .compatible = "fsl,ls1012a-pcie", .data = &ls1046_drvdata },
- { .compatible = "fsl,ls1021a-pcie", .data = &ls1021_drvdata },
- { .compatible = "fsl,ls1043a-pcie", .data = &ls1043_drvdata },
- + { .compatible = "fsl,ls1046a-pcie", .data = &ls1046_drvdata },
- { .compatible = "fsl,ls2080a-pcie", .data = &ls2080_drvdata },
- { .compatible = "fsl,ls2085a-pcie", .data = &ls2080_drvdata },
- + { .compatible = "fsl,ls2088a-pcie", .data = &ls2088_drvdata },
- + { .compatible = "fsl,ls1088a-pcie", .data = &ls2088_drvdata },
- { },
- };
-
- --- a/drivers/pci/host/pcie-designware.c
- +++ b/drivers/pci/host/pcie-designware.c
- @@ -478,6 +478,12 @@ int dw_pcie_wait_for_link(struct pcie_po
- return -ETIMEDOUT;
- }
-
- +void dw_pcie_disable_outbound_atu(struct pcie_port *pp, int index)
- +{
- + dw_pcie_writel_rc(pp, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_OUTBOUND | index);
- + dw_pcie_writel_rc(pp, PCIE_ATU_CR2, 0);
- +}
- +
- int dw_pcie_link_up(struct pcie_port *pp)
- {
- u32 val;
- --- a/drivers/pci/host/pcie-designware.h
- +++ b/drivers/pci/host/pcie-designware.h
- @@ -82,5 +82,6 @@ int dw_pcie_wait_for_link(struct pcie_po
- int dw_pcie_link_up(struct pcie_port *pp);
- void dw_pcie_setup_rc(struct pcie_port *pp);
- int dw_pcie_host_init(struct pcie_port *pp);
- +void dw_pcie_disable_outbound_atu(struct pcie_port *pp, int index);
-
- #endif /* _PCIE_DESIGNWARE_H */
- --- a/drivers/pci/pci.c
- +++ b/drivers/pci/pci.c
- @@ -454,7 +454,7 @@ struct resource *pci_find_parent_resourc
- pci_bus_for_each_resource(bus, r, i) {
- if (!r)
- continue;
- - if (res->start && resource_contains(r, res)) {
- + if (resource_contains(r, res)) {
-
- /*
- * If the window is prefetchable but the BAR is
- --- a/drivers/pci/pcie/portdrv_core.c
- +++ b/drivers/pci/pcie/portdrv_core.c
- @@ -44,52 +44,30 @@ static void release_pcie_device(struct d
- }
-
- /**
- - * pcie_port_msix_add_entry - add entry to given array of MSI-X entries
- - * @entries: Array of MSI-X entries
- - * @new_entry: Index of the entry to add to the array
- - * @nr_entries: Number of entries already in the array
- + * pcibios_check_service_irqs - check irqs in the device tree
- + * @dev: PCI Express port to handle
- + * @irqs: Array of irqs to populate
- + * @mask: Bitmask of port capabilities returned by get_port_device_capability()
- + *
- + * Return value: 0 means no service irqs in the device tree
- *
- - * Return value: Position of the added entry in the array
- */
- -static int pcie_port_msix_add_entry(
- - struct msix_entry *entries, int new_entry, int nr_entries)
- +int __weak pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask)
- {
- - int j;
- -
- - for (j = 0; j < nr_entries; j++)
- - if (entries[j].entry == new_entry)
- - return j;
- -
- - entries[j].entry = new_entry;
- - return j;
- + return 0;
- }
-
- /**
- * pcie_port_enable_msix - try to set up MSI-X as interrupt mode for given port
- * @dev: PCI Express port to handle
- - * @vectors: Array of interrupt vectors to populate
- + * @irqs: Array of interrupt vectors to populate
- * @mask: Bitmask of port capabilities returned by get_port_device_capability()
- *
- * Return value: 0 on success, error code on failure
- */
- -static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask)
- +static int pcie_port_enable_msix(struct pci_dev *dev, int *irqs, int mask)
- {
- - struct msix_entry *msix_entries;
- - int idx[PCIE_PORT_DEVICE_MAXSERVICES];
- - int nr_entries, status, pos, i, nvec;
- - u16 reg16;
- - u32 reg32;
- -
- - nr_entries = pci_msix_vec_count(dev);
- - if (nr_entries < 0)
- - return nr_entries;
- - BUG_ON(!nr_entries);
- - if (nr_entries > PCIE_PORT_MAX_MSIX_ENTRIES)
- - nr_entries = PCIE_PORT_MAX_MSIX_ENTRIES;
- -
- - msix_entries = kzalloc(sizeof(*msix_entries) * nr_entries, GFP_KERNEL);
- - if (!msix_entries)
- - return -ENOMEM;
- + int nr_entries, entry, nvec = 0;
-
- /*
- * Allocate as many entries as the port wants, so that we can check
- @@ -97,20 +75,13 @@ static int pcie_port_enable_msix(struct
- * equal to the number of entries this port actually uses, we'll happily
- * go through without any tricks.
- */
- - for (i = 0; i < nr_entries; i++)
- - msix_entries[i].entry = i;
- -
- - status = pci_enable_msix_exact(dev, msix_entries, nr_entries);
- - if (status)
- - goto Exit;
- -
- - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- - idx[i] = -1;
- - status = -EIO;
- - nvec = 0;
- + nr_entries = pci_alloc_irq_vectors(dev, 1, PCIE_PORT_MAX_MSIX_ENTRIES,
- + PCI_IRQ_MSIX);
- + if (nr_entries < 0)
- + return nr_entries;
-
- if (mask & (PCIE_PORT_SERVICE_PME | PCIE_PORT_SERVICE_HP)) {
- - int entry;
- + u16 reg16;
-
- /*
- * The code below follows the PCI Express Base Specification 2.0
- @@ -125,18 +96,16 @@ static int pcie_port_enable_msix(struct
- pcie_capability_read_word(dev, PCI_EXP_FLAGS, ®16);
- entry = (reg16 & PCI_EXP_FLAGS_IRQ) >> 9;
- if (entry >= nr_entries)
- - goto Error;
- + goto out_free_irqs;
-
- - i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
- - if (i == nvec)
- - nvec++;
- + irqs[PCIE_PORT_SERVICE_PME_SHIFT] = pci_irq_vector(dev, entry);
- + irqs[PCIE_PORT_SERVICE_HP_SHIFT] = pci_irq_vector(dev, entry);
-
- - idx[PCIE_PORT_SERVICE_PME_SHIFT] = i;
- - idx[PCIE_PORT_SERVICE_HP_SHIFT] = i;
- + nvec = max(nvec, entry + 1);
- }
-
- if (mask & PCIE_PORT_SERVICE_AER) {
- - int entry;
- + u32 reg32, pos;
-
- /*
- * The code below follows Section 7.10.10 of the PCI Express
- @@ -151,13 +120,11 @@ static int pcie_port_enable_msix(struct
- pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32);
- entry = reg32 >> 27;
- if (entry >= nr_entries)
- - goto Error;
- + goto out_free_irqs;
-
- - i = pcie_port_msix_add_entry(msix_entries, entry, nvec);
- - if (i == nvec)
- - nvec++;
- + irqs[PCIE_PORT_SERVICE_AER_SHIFT] = pci_irq_vector(dev, entry);
-
- - idx[PCIE_PORT_SERVICE_AER_SHIFT] = i;
- + nvec = max(nvec, entry + 1);
- }
-
- /*
- @@ -165,41 +132,54 @@ static int pcie_port_enable_msix(struct
- * what we have. Otherwise, the port has some extra entries not for the
- * services we know and we need to work around that.
- */
- - if (nvec == nr_entries) {
- - status = 0;
- - } else {
- + if (nvec != nr_entries) {
- /* Drop the temporary MSI-X setup */
- - pci_disable_msix(dev);
- + pci_free_irq_vectors(dev);
-
- /* Now allocate the MSI-X vectors for real */
- - status = pci_enable_msix_exact(dev, msix_entries, nvec);
- - if (status)
- - goto Exit;
- + nr_entries = pci_alloc_irq_vectors(dev, nvec, nvec,
- + PCI_IRQ_MSIX);
- + if (nr_entries < 0)
- + return nr_entries;
- }
-
- - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- - vectors[i] = idx[i] >= 0 ? msix_entries[idx[i]].vector : -1;
- -
- - Exit:
- - kfree(msix_entries);
- - return status;
- + return 0;
-
- - Error:
- - pci_disable_msix(dev);
- - goto Exit;
- +out_free_irqs:
- + pci_free_irq_vectors(dev);
- + return -EIO;
- }
-
- /**
- - * init_service_irqs - initialize irqs for PCI Express port services
- + * pcie_init_service_irqs - initialize irqs for PCI Express port services
- * @dev: PCI Express port to handle
- * @irqs: Array of irqs to populate
- * @mask: Bitmask of port capabilities returned by get_port_device_capability()
- *
- * Return value: Interrupt mode associated with the port
- */
- -static int init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
- +static int pcie_init_service_irqs(struct pci_dev *dev, int *irqs, int mask)
- {
- - int i, irq = -1;
- + unsigned flags = PCI_IRQ_LEGACY | PCI_IRQ_MSI;
- + int ret, i;
- + int irq = -1;
- +
- + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- + irqs[i] = -1;
- +
- + /* Check if some platforms owns independent irq pins for AER/PME etc.
- + * Some platforms may own independent AER/PME interrupts and set
- + * them in the device tree file.
- + */
- + ret = pcibios_check_service_irqs(dev, irqs, mask);
- + if (ret) {
- + if (dev->irq)
- + irq = dev->irq;
- + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- + if (irqs[i] == -1 && i != PCIE_PORT_SERVICE_VC_SHIFT)
- + irqs[i] = irq;
- + return 0;
- + }
-
- /*
- * If MSI cannot be used for PCIe PME or hotplug, we have to use
- @@ -207,41 +187,25 @@ static int init_service_irqs(struct pci_
- */
- if (((mask & PCIE_PORT_SERVICE_PME) && pcie_pme_no_msi()) ||
- ((mask & PCIE_PORT_SERVICE_HP) && pciehp_no_msi())) {
- - if (dev->irq)
- - irq = dev->irq;
- - goto no_msi;
- + flags &= ~PCI_IRQ_MSI;
- + } else {
- + /* Try to use MSI-X if supported */
- + if (!pcie_port_enable_msix(dev, irqs, mask))
- + return 0;
- }
-
- - /* Try to use MSI-X if supported */
- - if (!pcie_port_enable_msix(dev, irqs, mask))
- - return 0;
- -
- - /*
- - * We're not going to use MSI-X, so try MSI and fall back to INTx.
- - * If neither MSI/MSI-X nor INTx available, try other interrupt. On
- - * some platforms, root port doesn't support MSI/MSI-X/INTx in RC mode.
- - */
- - if (!pci_enable_msi(dev) || dev->irq)
- - irq = dev->irq;
- + ret = pci_alloc_irq_vectors(dev, 1, 1, flags);
- + if (ret < 0)
- + return -ENODEV;
-
- - no_msi:
- - for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++)
- - irqs[i] = irq;
- - irqs[PCIE_PORT_SERVICE_VC_SHIFT] = -1;
- + for (i = 0; i < PCIE_PORT_DEVICE_MAXSERVICES; i++) {
- + if (i != PCIE_PORT_SERVICE_VC_SHIFT)
- + irqs[i] = pci_irq_vector(dev, 0);
- + }
-
- - if (irq < 0)
- - return -ENODEV;
- return 0;
- }
-
- -static void cleanup_service_irqs(struct pci_dev *dev)
- -{
- - if (dev->msix_enabled)
- - pci_disable_msix(dev);
- - else if (dev->msi_enabled)
- - pci_disable_msi(dev);
- -}
- -
- /**
- * get_port_device_capability - discover capabilities of a PCI Express port
- * @dev: PCI Express port to examine
- @@ -378,7 +342,7 @@ int pcie_port_device_register(struct pci
- * that can be used in the absence of irqs. Allow them to determine
- * if that is to be used.
- */
- - status = init_service_irqs(dev, irqs, capabilities);
- + status = pcie_init_service_irqs(dev, irqs, capabilities);
- if (status) {
- capabilities &= PCIE_PORT_SERVICE_VC | PCIE_PORT_SERVICE_HP;
- if (!capabilities)
- @@ -401,7 +365,7 @@ int pcie_port_device_register(struct pci
- return 0;
-
- error_cleanup_irqs:
- - cleanup_service_irqs(dev);
- + pci_free_irq_vectors(dev);
- error_disable:
- pci_disable_device(dev);
- return status;
- @@ -469,7 +433,7 @@ static int remove_iter(struct device *de
- void pcie_port_device_remove(struct pci_dev *dev)
- {
- device_for_each_child(&dev->dev, NULL, remove_iter);
- - cleanup_service_irqs(dev);
- + pci_free_irq_vectors(dev);
- pci_disable_device(dev);
- }
-
- @@ -499,7 +463,6 @@ static int pcie_port_probe_service(struc
- if (status)
- return status;
-
- - dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", driver->name);
- get_device(dev);
- return 0;
- }
- @@ -524,8 +487,6 @@ static int pcie_port_remove_service(stru
- pciedev = to_pcie_device(dev);
- driver = to_service_driver(dev->driver);
- if (driver && driver->remove) {
- - dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n",
- - driver->name);
- driver->remove(pciedev);
- put_device(dev);
- }
- --- a/drivers/pci/quirks.c
- +++ b/drivers/pci/quirks.c
- @@ -3329,6 +3329,13 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_A
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x003c, quirk_no_bus_reset);
- DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATHEROS, 0x0033, quirk_no_bus_reset);
-
- +/*
- + * NXP (Freescale Vendor ID) LS1088 chips do not behave correctly after
- + * bus reset. Link state of device does not comes UP and so config space
- + * never accessible again.
- + */
- +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_FREESCALE, 0x80c0, quirk_no_bus_reset);
- +
- static void quirk_no_pm_reset(struct pci_dev *dev)
- {
- /*
- @@ -4679,3 +4686,11 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_IN
- DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2031, quirk_no_aersid);
- DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2032, quirk_no_aersid);
- DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, 0x2033, quirk_no_aersid);
- +
- +/* Freescale PCIe doesn't support MSI in RC mode */
- +static void quirk_fsl_no_msi(struct pci_dev *pdev)
- +{
- + if (pci_pcie_type(pdev) == PCI_EXP_TYPE_ROOT_PORT)
- + pdev->no_msi = 1;
- +}
- +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
- --- a/include/linux/pci.h
- +++ b/include/linux/pci.h
- @@ -1825,6 +1825,7 @@ void pcibios_release_device(struct pci_d
- void pcibios_penalize_isa_irq(int irq, int active);
- int pcibios_alloc_irq(struct pci_dev *dev);
- void pcibios_free_irq(struct pci_dev *dev);
- +int pcibios_check_service_irqs(struct pci_dev *dev, int *irqs, int mask);
-
- #ifdef CONFIG_HIBERNATE_CALLBACKS
- extern struct dev_pm_ops pcibios_pm_ops;
|